原题如下:
有一个抢数游戏,其规则是两人轮流报数,每次可以报1 个,2 个,3 个,4个数,但不许不报也不许多报,如果第一个人报1,2 或1,2,3,第二个人接着往下报,然后第一个人接着往下报,以此类推,那么第一个人第一次应该报( )才可能稳抢到1024.
1024,有点陡,我们先换一个小一点的数来分析:
抢“30”游戏,两人从1开始轮流数数,最少数1个数,最多数3个数,谁数到30谁赢,怎么数?
分析(假设其中一个人是我,且我想赢):
如果数到30就赢,且每次只能数1-3个数,那只需要数到26就可以了
当我数到26,此时对方 加上1到3中的任何一个数都到不了30,反而只需要等对方数完后我再走一步就到30了,比如对方数2,就到28我再数个2就到30,对方数3,我数1就到30...
怎样才能走到26呢,想一想:首先我们观察一下每次报的数:1、2、3,这三个数能不能两两组合成一个固定的数,我们发现,1+3=4, 2+2=4,所以这个固定的组合数就是 4 (自己去多写几个数来找找规律)
由此得方案:目标数 30 减去“最多数 3 个数”中的 3,再减 1 得 26
如果 26 能被组合数 4 整除,则只需要对方先出,然后我每次出一个和对方相加和为 4 的数即可,最后我一定能数到 30
但很明显此处的 26 不能被组合数 4 整除,所以:走第一步时一定要掌握主动权,将 26 钝化为可以被 4 整除的数,即 26 减去我第一步走的数后需要能被 4 整除。
故我要走第一步,且必须数 2 。26 - 2=24, 得到的 24 能被 4 整除
接下来只需要等对方出,(随便出1-3中哪个数),我方只需要出一个和对方数字相加为 4 的数即可胜利! (24/4=6 。即六轮后我就能数到30)
答案:
我先数,数2;接下来我只需要数一个和对方相加为 4 的数即可我赢、
接下来我们再来分析 1024 这道题:
方案如下:
1024-4-1 = 1019 即只需要拿到1019即可赢
组合数为:4+1 = 5
1019 除不尽组合数 5
故我先数,数4, (1019-4=1015,这样1015才能被5整除)
等对方数,然后我再数一个与对方的数和为5的数即可
接下来我们用 python 来实现:
以下代码直接复制即可运行(python3.7)
import random
def myFunc(n, stepper):
"""
:参数 n: 目标数
:参数 stepper: 步进范围(可以走几步的最大值)
:返回值: (flag,first_num,total_stepper)
第一个出还是第二个出:flag=1,第一个出;flag = 0第二个出
如果第一个出:第一次出多少 first_num
如果第二个出:出的数总和为多少 total_stepper
"""
total_stepper = stepper + 1 # 组合数
if n % total_stepper == 0: #能整除的情况
print("目标数:{},步进范围:{},组合数:{}".format(n, stepper, total_stepper))
flag = 0 # 对方先出
return (flag, 0, total_stepper)
else:
v_num = n - stepper - 1 # 拿到v_num就一定能数到目标数
if v_num % total_stepper == 0: # v_num能被组合数整除的情况
flag = 0 # 对方先出
print("目标数:{}, 步进范围:{}, 组合数:{}, 是否第一个出:{}".format(n, stepper, total_stepper, flag))
return (flag, 0, total_stepper)
else:
flag = 1 # 我方先出
first_num = v_num % total_stepper # 我第一次数的数,这个数将v_num钝化为可以被组合数整除的数
print("目标数:{}, 步进范围:{}, 组合数:{}, 是否第一个出:{}, 出多少:{}".format(n, stepper, total_stepper, flag, first_num))
return (flag, first_num, total_stepper)
print(myFunc(1024,4))
运行结果截图:
上面的代码每一行都有注释,结合着上面的分析来看就会很容易了。
最后的返回值为(是否先数,先数多少,组合数为多少)
这个函数可不是只能算1024这一道题哦,以后遇到这一类型的题,我们都可以将我们的的目标数和步进范围输入函数,得到我们想要的答案。
------------ 啦啦啦啦。。。python 在手,妈妈再也不用担心我的数学规律题了! ------------
比如我们马上就可以试一试其他的组合:
例1:
两个人数数,谁先数到2048谁就胜利,每次可以数1-6中的任一个数。问谁会赢,怎么数?
运行结果为:
答案为:第一个人赢。第一个人第一次数4,后面只需要数一个与对方的数和为 7 的数即可
例2:
两个人数数,谁先数到 2019 谁就胜利,每次可以数 1-18 中的任一个数。问谁会赢,怎么数?
运行结果为:
答案为:第一个人赢。第一个人第一次数5,后面只需要数一个与对方的数和为 19 的数即可
例3
两个人数数,谁先数到 2020 谁就胜利,每次可以数 1-9 中的任一个数。问谁会赢,怎么数?
运行结果为:
答案为:第二个人赢。第二个人只需要数一个与第一个人数的数和为 10 的数即可赢
这样的结果是不是还不够友好,那接下来我们再用 python 来模拟一下整个数数过程吧。
先来个温柔一点的数到 30 就可以了。
程序代码如下:
import random
def myFunc(n, stepper):
total_stepper = stepper + 1 # 组合数
if n % total_stepper == 0: #能整除的情况
flag = 0 # 对方先出
return (flag, 0, total_stepper)
else:
v_num = n - stepper - 1 # 拿到v_num就一定能数到目标数
if v_num % total_stepper == 0: # v_num能被组合数整除的情况
flag = 0 # 对方先出
return (flag, 0, total_stepper)
else:
flag = 1 # 我方先出
first_num = v_num % total_stepper # 我第一次数的数,这个数将v_num钝化为可以被组合数整除的数
return (flag, first_num, total_stepper)
def myTest(n, stepper):
flag, first_num, total_stepper = myFunc(n, stepper) #调用myFunc函数得到:是否先数、第一步数多少、组合数
print((flag, first_num, total_stepper))
i = 1 # 设定参照数
cnt = 0 # 数数容器
while True:
if flag == 1 and i == 1:
data = first_num
people = "我"
elif flag == i % 2 and i != 1:
people = "我"
data = total_stepper - data
else:
people = "码小易"
data = random.randint(1, total_stepper-1)
cnt += data
print("{}第{}次出:{}".format(people, i, data))
if cnt == n:
break
i += 1
if __name__ == "__main__":
myTest(30, 3)
运行结果截图:
以上即将数数的全过程模拟得清清楚楚了,怎么样,现在来试试1024吧!
例4:
数到1024赢,每次数1-4中的一个数:
经过以上两个程序,是不是觉得 python 已经把这道乃至于这一类磨人的数学题都梳理得服服帖帖的了呢,以后再遇到同样的数学难题,大家一定不要忘了用我们的 python 来实现,写几行代码不仅能解决一道绕半天都绕不出来的题,还能直接搞定一个类型的数学题。
接下来就等着接收来自班里其他同学膜拜的眼神吧!
笔者会不定时的更新一些跟python相关又和数学相关的一些有趣的程序,喜欢就关注我吧。
特别警示:本文为作者原创作品,禁止不经过本人同意就将其转载用于商业用途,否则将予以追究。出于学习分享转载请附上出处链接,谢谢!