鱼C论坛

 找回密码
 立即注册
查看: 5997|回复: 8

[技术交流] 小练习:20170229 找出唯一具有循环性质的6个四位数定形数集合的元素和

[复制链接]
发表于 2017-2-19 21:13:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 冬雪雪冬 于 2017-2-27 11:12 编辑

从现在开始我们要开展一批欧拉计划的习题练习。
其实在我们论坛中已有欧拉计划的板块,可能有些鱼油还没注意到。
什么是欧拉计划:http://bbs.fishc.com/thread-60405-1-1.html
我们欧拉板块现已给出了200余题,这批练习将从欧拉计划中选题。其实用python语言完成有很多的优势,可以更简洁更方便的实现。
如果大家有兴趣也可浏览欧拉的英文网站:https://projecteuler.net/archives
这里已经有了500余题。


                               
登录/注册后可看大图


题目要求:
以python语言完成,如果是python2请注明。
程序以代码文字格式发帖。
注重程序效率和创意。
答题在一周内完成,即2.27 10:00之前,其后将公开大家的答案,并评比成绩。

另程序和答案可以在网上搜到,希望大家独立完成。题目不难,大家看看谁的效率高。

----回帖需写明解题思路,鼓励在程序



题目:

三角形数,四角形数,五角形数,六角形数,七角形数和八角形数都是定形数,他们分别由以下公式产生:


                               
登录/注册后可看大图


三个四位数形成的有序集合: 8128, 2882, 8281,有三个有趣的性质:

1. 这个集合是循环的:每个数的后两位数是下一个数的前两位数,包括第三个和第一个的关系。
2. 三种定形数中的每一种都能被这三个数中的一个不同的数代表:三角形数

                               
登录/注册后可看大图
, 四角形数

                               
登录/注册后可看大图
, 和五角形数

                               
登录/注册后可看大图

3. 这是唯一具有以上性质的四位数的集合。

找出唯一的一个六个四位数的循环集合,使得从三角形数到八角形数中的每一种都能由该集合中的一个不同的数代表。

求这个集合中元素之和。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2017-2-19 21:50:47 | 显示全部楼层
这题其实还是一道搜索算法题,当然如上周所说的用“深度优先”,“广度优先”和“A*算法”都能解决。

我就用A*算法吧,程序简单,效率也不低。

思路很简单,先建立从三角数到八角数所有的候选数列表,做成一个二维数组。
然后从第一层出发(假设为三角数),然后依次搜索下一层,看能否找到匹配的数,如果可以则加入候选列表,以此类推。列表从后往前开始pop()
代码:
  1. #coding:utf-8
  2. import time
  3. start = time.time()
  4. lists = [
  5. [str(int(n*(n+1)/2)) for n in range(200) if 1000<=n*(n+1)/2<10000],
  6. [str(int(n*n)) for n in range(200) if 1000<=n*n<10000],
  7. [str(int(n*(3*n-1)/2)) for n in range(200) if 1000<=n*(3*n-1)/2<10000],
  8. [str(int(n*(2*n-1))) for n in range(200) if 1000<=n*(2*n-1)<10000],
  9. [str(int(n*(5*n-3)/2)) for n in range(200) if 1000<=n*(5*n-3)/2<10000],
  10. [str(int(n*(3*n-2))) for n in range(200) if 1000<=n*(3*n-2)<10000]]

  11. def ispair(s1,s2):
  12.         return True if s1[2:]==s2[:2] else False

  13. queue = [[each,[0]] for each in lists[0]]
  14. while len(queue) >0:
  15.         q = queue.pop()
  16.         row = len(q)-1
  17.         if row == 6:
  18.                 if ispair(q[-2],q[0]):
  19.                         print('Bingo!', q[:-1])
  20.                         break
  21.         else:
  22.                 for i in range(1,6):
  23.                         if i in q[-1]: continue
  24.                         for p in lists[i]:
  25.                                 if ispair(q[-2],p) and i not in q[-1]:
  26.                                         l = [e for e in q[-1]]
  27.                                         l.append(i)
  28.                                         queue.append(q[:-1]+[p]+[l])
  29.                                         break
  30. print('Time used: %.3f sec' % (time.time()-start))
复制代码

输出:
Bingo! ['8256', '5625', '2512', '1281', '8128', '2882']
Time used: 0.015 sec

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-20 00:19:41 | 显示全部楼层
本帖最后由 飘飞的白杨 于 2017-2-24 00:17 编辑

递归
  1. import sys
  2. from time import time

  3. ttt = time()

  4. def getlist(lam):
  5.     '''获取x角形数的列表'''
  6.     l = []
  7.     for i in range(1, 1000):
  8.         n = lam(i)
  9.         if n < 1000: continue
  10.         if n >= 10000: return l
  11.         l.append(n)

  12. def progrem61_2(numbers):
  13.     if len(numbers) == 6 and numbers[-1] % 100 == numbers[0] // 100:
  14.         print(numbers, sum(numbers), "\ntime:%.5fs"%(time()-ttt))
  15.         sys.exit()
  16.     for i in range(6):
  17.         if flag[i]: continue
  18.         flag[i] = True
  19.         for j in p[i]:
  20.             if j // 100 == numbers[-1] % 100:
  21.                 progrem61_2(numbers+[j])
  22.         flag[i] = False

  23. def progrem61_1():
  24.     flag[-1] = True
  25.     # 八角形数最少,从八角形数中选择第一个数
  26.     for i in p8:
  27.         progrem61_2([i])

  28. p3 = getlist(lambda n: n * (n + 1) // 2)
  29. p4 = getlist(lambda n: n * n)
  30. p5 = getlist(lambda n: n * (3 * n - 1) // 2)
  31. p6 = getlist(lambda n: n * (2 * n - 1))
  32. p7 = getlist(lambda n: n * (5 * n - 3) // 2)
  33. p8 = getlist(lambda n: n * (3 * n - 2))
  34. p = [p3, p4, p5, p6, p7, p8]
  35. flag = [False] * 6 # 标志数组,用于判断是否遍历过
  36. progrem61_1()
复制代码

评分

参与人数 1荣誉 +15 鱼币 +15 收起 理由
冬雪雪冬 + 15 + 15

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-20 13:41:54 | 显示全部楼层
for用的太多,自己都有点绕了
  1. def fen(jh):          #把xxxx分成[xx,xx]的形式,4位数分成两个两位数,排除后面的两位数小于10的情况
  2.     ls=[]
  3.     for i in jh:
  4.         if i%100>9:
  5.             ls.append([i//100,i%100])
  6.     return ls

  7. jhsan,jhsi,jhwu,jhliu,jhqi,jhba=[],[],[],[],[],[]

  8. for x in range(15,150):#根据题目的公式,把6种形式的4位数分别放入对应集合
  9.     if x*(x+1)/2>1000 and x*(x+1)/2<9999      :jhsan.append(int(x*(x+1)/2))
  10.     if x**2>1000 and x**2<9999                :jhsi.append(x**2)
  11.     if x*(3*x-1)/2>1000 and x*(3*x-1)/2<9999  :jhwu.append(int(x*(3*x-1)/2))
  12.     if x*(2*x-1)>1000 and x*(2*x-1)<9999      :jhliu.append(x*(2*x-1))
  13.     if x*(5*x-3)/2>1000 and x*(5*x-3)/2<9999  :jhqi.append(int(x*(5*x-3)/2))
  14.     if x*(3*x-2)>1000 and x*(3*x-2)<9999      :jhba.append(x*(3*x-2))

  15. jhsan,jhsi,jhwu,jhliu,jhqi,jhba=fen(jhsan),fen(jhsi),fen(jhwu),fen(jhliu),fen(jhqi),fen(jhba) #6个集合的数分裂一下
  16. cs=[[],[],[],[],[],[]]#数字分别带入之后测试是否满足条件的列表
  17. huizong0=[jhsan,jhsi,jhwu,jhliu,jhqi,jhba]#汇总6个集合的数,huizong0-5决定第一个数到第六个数的取值范围
  18. for s0 in huizong0:                       #huizong0里面是第一个数的可选范围
  19.     huizong1=huizong0.copy()
  20.     huizong1.remove(s0)
  21.     for s1 in huizong1:                   #huizong1里面是把huizong0里面剔除一个集合之后的汇总,也就是第二个数的备选范围
  22.         huizong2=huizong1.copy()
  23.         huizong2.remove(s1)
  24.         for s2 in huizong2:               #下面直到huizong5都是以此类推
  25.             huizong3=huizong2.copy()
  26.             huizong3.remove(s2)
  27.             for s3 in huizong3:
  28.                 huizong4=huizong3.copy()
  29.                 huizong4.remove(s3)
  30.                 for s4 in huizong4:
  31.                     huizong5=huizong4.copy()
  32.                     huizong5.remove(s4)   
  33.                     for s5 in huizong5:
  34.                         for cs[0] in s0:          #从这里开始是真正的验证排序是否符合题目要求,符合要求之后每两个两位数合成一个4位数
  35.                             for cs[1] in s1:
  36.                                 if cs[0][1]!=cs[1][0]:continue
  37.                                 for cs[2] in s2:
  38.                                     if cs[1][1]!=cs[2][0]:continue
  39.                                     for cs[3] in s3:
  40.                                         if cs[2][1]!=cs[3][0]:continue
  41.                                         for cs[4] in s4:
  42.                                             if cs[3][1]!=cs[4][0]:continue
  43.                                             for cs[5] in s5:
  44.                                                 if cs[4][1]!=cs[5][0]:continue
  45.                                                 if cs[5][1]==cs[0][0]:
  46.                                                     cs=[cs[0][0]*100+cs[0][1],cs[1][0]*100+cs[1][1],cs[2][0]*100+cs[2][1],cs[3][0]*100+cs[3][1],cs[4][0]*100+cs[4][1],cs[5][0]*100+cs[5][1]]
  47.                                                     print(cs,sum(cs))
  48.                                                     exit()
复制代码

  1. == RESTART: C:\Users\ASUS\AppData\Local\Programs\Python\Python35-32\test.py ==
  2. [8256, 5625, 2512, 1281, 8128, 2882] 28684
  3. >>>
复制代码


评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-20 15:04:50 | 显示全部楼层
本帖最后由 Craigyu 于 2017-2-20 15:09 编辑

数不多,就把数全列出来一个一个找。找到下一个添加进表,找不到下一个则将前一个POP掉,长度为6时比较第一个和最后一个数,为TRUE则结束。
[1281, 8128, 2882, 8256, 5625, 2512]
28684
0.0312192440032959

  1. from time import time
  2. start = time()

  3. lst3 = []
  4. lst4 = []
  5. lst5 = []
  6. lst6 = []
  7. lst7 = []
  8. lst8 = []

  9. for n in range(45, 141):
  10.     lst3.append(int(n * (n + 1)/2))
  11. for n in range(32, 100):
  12.     lst4.append(n * n)
  13. for n in range(26, 82):
  14.     lst5.append(int(n * (3 * n - 1)/2))
  15. for n in range(23, 71):
  16.     lst6.append(n * (2 * n - 1))
  17. for n in range(21, 64):
  18.     lst7.append(int(n * (5 * n - 3)/2))
  19. for n in range(19, 59):
  20.     lst8.append(n * (3 * n - 2))

  21. lst = [lst3, lst4, lst5, lst6, lst7]

  22. nums = []
  23. pls = [8]

  24. def judge(m, n):
  25.     if str(m)[2:] == str(n)[:2]:
  26.         return True

  27. def polygonal(num):
  28.     if len(nums) == 6:
  29.         if judge(nums[-1], nums[0]) == True:
  30.             return True
  31.     else:
  32.         for i in range(5):
  33.             if i not in pls:
  34.                 for each in lst[i]:
  35.                     if judge(num, each) == True:
  36.                         pls.append(i)
  37.                         nums.append(each)
  38.                         flag = polygonal(nums[-1])
  39.                         if flag != True:
  40.                             pls.pop()
  41.                             nums.pop()
  42.                         else:
  43.                             return True

  44. for num in lst8:
  45.     nums.append(num)
  46.     if polygonal(num) == True:
  47.         print(nums)
  48.         break
  49.     else: nums.pop()
  50. print(sum(nums))
  51. print(time()-start)
复制代码

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-23 08:43:54 | 显示全部楼层
这也是真的无聊 每天不是 质数就是 三角数 四角数。。。。旋转的质数,,质数的和 质数的差 质数的乘 质数的商,,python 的算法 就是为了处理质数 的? 其他的题目都死绝了么?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 0 反对 1

使用道具 举报

发表于 2017-2-23 11:03:50 | 显示全部楼层
#这是我第一次在fishC论坛发帖,所以如果有什么不正确的地方还希望大佬可以指点一下,尽管我很早就开始看fishC的视频来学习编程,但是我是最近才开始学习的Python,所以代码里面可能有很多令人哭笑不得的地方,,各种变量和函数的命名也不太好,,望谅解
#关于这道题目,我之前没有见到过,也没有参考过网上的答案,,所以我很自知并不是最佳算法,但是我还是要发出来,鼓励一下自己

我的思路是 先把所有的六组定性数算出来然后从这六组数中找出所需要的list
六组(3-8组)定性数的过程并不复杂,这里不再说,具体看代码里面的注释
找出符合要求的数字,我的思路是 这样的, 要找到六个定性数,我先拿一组(比如3组)定性数,然后从这一组里面拿出来一个数字A,然后在剩下的数字里面找到和A首尾相接的数字(比如4组的B),然后在剩下的组(5,6,7,8)里面找和B首尾相接的数字,,直到找出最后一个数字(G),然后验证G是否和A是首尾相接,,
最终结果不会给出每一个数字来自哪组定性数,,还需要额外的过程来得知
上代码:
先贴一下环境:
Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。

C:\Users\****>python
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (In
tel)] on win32

代码:
  1. def findNum(x):                #定义一个算定性数的函数
  2.         def _res(n):        #计算用的函数
  3.                 #所有定性数的计算公式可以用这个来代表,,
  4.                 #如果是x角定性数,那么公式就是n*((x-2)*n+(4-x))/2
  5.                 #
  6.                 return int(n*((x-2)*n+(4-x))/2)               
  7.         _list=[]                #一个空列表用来存一组定性数
  8.         i=1
  9.         _tmp=_res(i)        #先算出第一个
  10.         while _tmp<10000:                #while循环来算出所有小于10000的定性数
  11.                 #我们所要求的数字并不能用上所有的定性数
  12.                 #上面的while条件限制了要求的定性数的长度
  13.                 #因为要求首尾相接的四位数,所以后两位小于10的数字肯定不符合要求
  14.                 if _tmp>999 and _tmp%100>9:                #排除掉三位以下的和后两位小于10的
  15.                         _list.append(_tmp)
  16.                 i+=1
  17.                 _tmp=_res(i)
  18.         return _list        #返回的存了一组符合要求定性数的list
  19. #先把所有的数字算出来再说后面的
  20. numList=[]                        #在这里我打算把所有的8组定性数存到这个list里面
  21. for i in range(3,9):
  22.         numList.append(findNum(i))

  23.        
  24. num8=numList.pop()        #这里是文字介绍里面的先拿一组数出来



  25. def fin(num,ii,mylist):               
  26. #这个函数用来递归找数,num是首尾相接里面的首
  27. #ii后面说
  28. #mylist是(文字介绍里面说的)剩下的数组
  29.         if len(mylist)==0:
  30.                 return []
  31.         else:
  32.                 for sublist in mylist:                #遍历外层数组拿出一组定性数
  33.                         for i in sublist:                #遍历内层数组,拿出一个数字
  34.                                 tmplist=[]+mylist        #先把大数组留个备份,这里不能直接用=赋值
  35.                                 #首尾相接就是前一个数字对100取余的结果和后一个数字整除100的结果相等
  36.                                 if (num%100)==(i//100):               
  37.                                         if len(mylist)==1:                #验证是不是最后一个数字
  38.                                                 #如果是最后一个数字的话,还需要和A(文字介绍中的)首尾相接
  39.                                                 #这里的ii就是函数的第二个参数,将来的数字A
  40.                                                 if i%100!=ii//100:       
  41.                                                         continue
  42.                                         tmplist.remove(sublist)        #如果有匹配的值的话,那么这一组定性数都不应该再出现
  43.                                         _res= [i]+fin(i,ii,tmplist)        #在这里递归
  44.                                         if len(_res)==len(mylist):        #通过长度来保证这一列数字有六组定性数的成员
  45.                                                 return _res
  46.         return []
  47.                
  48. for i in num8:
  49.         resultList=[i]+fin(i,i,numList)
  50.         if len(resultList)==6:                #只有有结果的resultList的len是6,其他都只有一个
  51.                 print(sum(resultList))        #打印结果
复制代码

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-26 23:35:16 | 显示全部楼层
本帖最后由 wangzhenas 于 2017-2-27 20:48 编辑

主要思路就是递归,传递的参数是 (当前值(int), 已形成的链(list),还剩下哪几种数没找到(set))
把各种计算方法保存为lambda函数,用列表生成式生成5个字典{前两位:数值}
findchain函数通过对set做循环找出一条有效的链
耗时:0.01s
答案: 28684
i5-3210M

  1. import time
  2. t = time.time()

  3. gens = (lambda n: n*n,
  4.         lambda n: n*(3*n-1)//2,
  5.         lambda n: n*(2*n-1),
  6.         lambda n: n*(5*n-3)//2,
  7.         lambda n: n*(3*n-2))

  8. num_l = [{gen(i)//100:gen(i) for i in range(1,100) if 1000<gen(i)<10000} for gen in gens]

  9. def find_chain(num, l=None, sel_set=set(range(len(gens)))):   
  10.     chain_l = [num] if not l else l
  11.     if sel_set:
  12.         for p in sel_set:
  13.             if num%100 in num_l[p]:
  14.                 find_chain(num_l[p][num%100], chain_l+[num_l[p][num%100]], sel_set-{p})
  15.             else:
  16.                 continue
  17.     else:
  18.         if l[-1]%100 == l[0]//100:
  19.             print(l, sum(l), time.time()-t)
  20.         
  21.         
  22. for value in [n*(n+1)//2 for n in range(200+1) if 1000<n*(n+1)//2<10000]:
  23.     find_chain(value)   
复制代码

评分

参与人数 1荣誉 +15 鱼币 +15 收起 理由
冬雪雪冬 + 15 + 15

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-2-28 09:16:46 | 显示全部楼层
题目没看懂
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-3 04:26

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表