鱼C论坛

 找回密码
 立即注册
查看: 3472|回复: 12

[技术交流] 小练习:20160815 找出所有能写成 pandigital 数字乘积的数字之和

[复制链接]
发表于 2016-8-14 21:00:04 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 冬雪雪冬 于 2016-8-22 10:07 编辑

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


                               
登录/注册后可看大图

好了言归正传,我们开始做小练习。




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

另程序和答案可以在网上搜到,希望大家独立完成。

----除列出程序外,请给出输出的结果。----


题目:

如果一个 n 位数使用了 1 到 n 中每个数字且只使用了一次,我们称其为 pandigital。例如,15234 这个五位数,是 1 到 5 pandigital 的。

7254 是一个不寻常的数,因为:39 × 186 = 7254 这个算式的乘数,被乘数和乘积组成了一个1到 9 的 pandigital 组合。

找出所有能够组合成 1 到 9 pandigital 的乘法算式中乘积的和。

提示: 有的乘积数字能够被多个乘法算式得到,所以在计算时要记得只计算它们一次。

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2016-8-14 21:07:30 | 显示全部楼层
本帖最后由 冬雪雪冬 于 2016-8-14 22:16 编辑

答案是:45228
我的思路是:
利用排列函数将1~9的排列列出,做循环。
下一步就是乘号放在哪里?
考虑了一下只有几个可能:
1位*4位=4位
2位*3位=4位
3位*3位=3位(犯了个低级错误,这个怎么能成立呢!,应该去掉!,程序先不改了,好在没影响最终的结果。)
(反过来的4位*1位,因排列时会出现与1位*4位相同的结果,故忽略,其他类似)
相同的乘积只计一次,所以用set来存放。
  1. from time import time
  2. from itertools import permutations
  3. start = time()
  4. data = permutations('123456789')
  5. set1 = set()
  6. for i in data:
  7.     product = int(i[5] + i[6] + i[7] + i[8])
  8.     if int(i[0]) * int(i[1] + i[2] + i[3] + i[4]) == product:
  9.         set1.add(product)
  10.     product = int(i[5] + i[6] + i[7] + i[8])
  11.     if int(i[0] + i[1]) * int(i[2] + i[3] + i[4]) == product:
  12.         set1.add(product)
  13.     product = int(i[6] + i[7] + i[8])
  14.     if int(i[0] + i[1] + i[2]) * int(i[3] + i[4] + i[5])== product:
  15.         set1.add(product)
  16. print(sum(set1))
  17. print(time() - start)
  18.               
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-14 21:56:36 | 显示全部楼层
答案: 45228
思路: 要求1-9的数字各出现一次,那么符合形式的乘法如下:
x * xxxx = xxxx
xx * xxx = xxxx
而xx * xx 不可能得到5位数,xxx * xxx 不可能得到3位数, 因此缩小了迭代法的范围

  1. import time
  2. t,num_s = time.time(),set()

  3. for i in range(1,99):
  4.     for j in range(1000//i,10000//i):
  5.         s = str(i)+str(j)+str(i*j)
  6.         if len(s)==9:
  7.             s = set(s)
  8.             if len(s)==9 and '0' not in s:
  9.                 num_s.add(i*j)
  10.                 print(i,j,i*j)
  11.                
  12. print(sum(num_s),time.time()-t)
复制代码

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-15 14:00:35 | 显示全部楼层
好有意思的样子

评分

参与人数 1鱼币 -5 收起 理由
冬雪雪冬 -5 请不要无意义灌水!

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-15 14:28:14 | 显示全部楼层
  1. import itertools
  2. list0 = []
  3. def a(l):
  4.     x1 = l[0]
  5.     x2 = l[0]*10+l[1]
  6.     y1 = l[1]*1000+l[2]*100+l[3]*10+l[4]
  7.     y2 = l[2]*100+l[3]*10+l[4]
  8.     z = l[5]*1000+l[6]*100+l[7]*10+l[8]
  9.     if x1*y1 == z or x2*y2 == z:
  10.         return z
  11.     else:
  12.         return False
  13. for l in itertools.permutations([1,2,3,4,5,6,7,8,9],9):
  14.     n =a(l)
  15.     if n != False and n not in list0:
  16.         list0.append(n)
  17. print(sum(list0))
复制代码

答案:45228

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-15 16:20:57 | 显示全部楼层
本帖最后由 bacon6581 于 2016-8-19 12:06 编辑
  1. from time import time
  2. start=time()

  3. a=set()
  4. result=0
  5. #用的是穷举法,即先获取每一个9位数,再去判断是否属于pandigital 组合
  6. #P9取9 即9!=362880种取法
  7. #第一位数字有9种取法(1-9)
  8. #取出来的数字放在列表‘c’里(这时c里只有刚放进来的一个数字)
  9. for i1 in range(0,9):
  10.     b=[1,2,3,4,5,6,7,8,9]
  11.     c=[]
  12.     c.append(b.pop(i1))
  13.     b1=b[:]
  14.     c1=c[:]
  15. #第二位数字有8种取法(第一位数字已取走的那个数字,就不能再取了)
  16. #取出来的数字放在列表‘c’里(这时c里有两个数字了)
  17.     for i2 in range(0,8):
  18.         b=b1[:]
  19.         c=c1[:]
  20.         c.append(b.pop(i2))        
  21.         b2=b[:]
  22.         c2=c[:]
  23. #第三位数字有7种取法(第一、二位数字已取走的那个数字,就不能再取了)
  24. #取出来的数字放在列表‘c’里(这时c里有三个数字了)
  25. #下同
  26.         for i3 in range(0,7):
  27.             b=b2[:]
  28.             c=c2[:]
  29.             c.append(b.pop(i3))
  30.             b3=b[:]
  31.             c3=c[:]
  32.             for i4 in range(0,6):
  33.                 b=b3[:]
  34.                 c=c3[:]
  35.                 c.append(b.pop(i4))
  36.                 b4=b[:]
  37.                 c4=c[:]
  38.                 for i5 in range(0,5):
  39.                     b=b4[:]
  40.                     c=c4[:]
  41.                     c.append(b.pop(i5))
  42.                     b5=b[:]
  43.                     c5=c[:]
  44.                     for i6 in range(0,4):
  45.                         b=b5[:]
  46.                         c=c5[:]
  47.                         c.append(b.pop(i6))
  48.                         b6=b[:]
  49.                         c6=c[:]
  50.                         for i7 in range(0,3):
  51.                             b=b6[:]
  52.                             c=c6[:]
  53.                             c.append(b.pop(i7))
  54.                             b7=b[:]
  55.                             c7=c[:]
  56.                             for i8 in range(0,2):
  57.                                 b=b7[:]
  58.                                 c=c7[:]
  59.                                 c.append(b.pop(i8))
  60. #前面8个数字都确定了,那最后一位只有一种取法了:P1取1=1
  61. #直接把剩余的一个数字,放入列表‘c’里边
  62.                                 c.append(b[0])
  63. #产生pandigital 组合的只可能会是以下4种情况:
  64. #1位数 * 4位数 = 5位数
  65. #2位数 * 3位数 = 5位数
  66. #4位数 * 1位数 = 5位数
  67. #3位数 * 2位数 = 5位数
  68. #如果有123456789组合,则必有234516789组合
  69. #即1*2345==5678(第一个组合的第一种判断) 与2345*1==5678(第二个组合的第3种判断)属重复判断
  70. #故注释掉后两种判断
  71. #判断成立,即该数值属pandigital 组合,并将结果放入集合'a'中(不用操心会有重复值)
  72.                                 if(((c[0]*(c[1]*1000+c[2]*100+c[3]*10+c[4]))==c[5]*1000+c[6]*100+c[7]*10+c[8])
  73.                                    or ((c[0]*10+c[1])*(c[2]*100+c[3]*10+c[4])==c[5]*1000+c[6]*100+c[7]*10+c[8])):
  74.                                    #or ((c[0]*100+c[1]*10+c[2])*(c[3]*10+c[4])==c[5]*1000+c[6]*100+c[7]*10+c[8])
  75.                                    #or ((c[0]*1000+c[1]*100+c[2]*10+c[3])*(c[4])==c[5]*1000+c[6]*100+c[7]*10+c[8])):
  76.                                     a.add(c[5]*1000+c[6]*100+c[7]*10+c[8])
  77. #将集合a里的所有数值累加一遍,即所求之结果!
  78. for k in a:
  79.     result+=k
  80. print(result)
  81. print(time()-start)
复制代码

  1. >>> ================================ RESTART ================================
  2. >>>
  3. 45228
  4. 22.254349946975708
  5. >>> ================================ RESTART ================================
  6. >>>
  7. 45228
  8. 15.573134183883667
  9. >>>
复制代码


中文注释后加的,要是注释有报错,麻烦版主删掉再试!
注释掉两行,速度快了7秒

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-17 16:17:07 | 显示全部楼层
  1. #12*798为4位数,79*123为4位数
  2. #范围为(12~79),(798~123)
  3. import itertools as it
  4. count = 0
  5. a = list(it.permutations(range(1,10),3))
  6. b = list(it.permutations(range(1,10),2))
  7. for i in a:
  8.     for j in b:
  9.         if set(i) & set(j) == set():
  10.             c = int(str(i[0])+str(i[1])+str(i[2]))
  11.             d = int(str(j[0])+str(j[1]))
  12.             if c <798 and d <79:
  13.                 mult = c * d
  14.                 if mult < 10000:
  15.                     mult_str = str(mult)
  16.                     mult_set = {int(mult_str[0]),int(mult_str[1]),int(mult_str[2]),int(mult_str[3])}
  17.                     if 0 not in mult_set and len(mult_set) == 4:
  18.                         union = set(i).union(set(j))
  19.                         if union & mult_set == set():
  20.                             count += 1
  21.                             print('%d x %d = %d'%(c,d,mult))
  22.             else:
  23.                 continue
  24. print('共有%d个这样的结果'%count)
复制代码


结果:
138 x 42 = 5796
157 x 28 = 4396
159 x 48 = 7632
186 x 39 = 7254
198 x 27 = 5346
297 x 18 = 5346
483 x 12 = 5796
共有7个这样的结果
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-17 16:24:10 | 显示全部楼层
看错了,原来是求和的
  1. import itertools as it
  2. total = []
  3. a = list(it.permutations(range(1,10),3))
  4. b = list(it.permutations(range(1,10),2))
  5. for i in a:
  6.     for j in b:
  7.         if set(i) & set(j) == set():
  8.             c = int(str(i[0])+str(i[1])+str(i[2]))
  9.             d = int(str(j[0])+str(j[1]))
  10.             if c <798 and d <79:
  11.                 mult = c * d
  12.                 if mult < 10000:
  13.                     mult_str = str(mult)
  14.                     mult_set = {int(mult_str[0]),int(mult_str[1]),int(mult_str[2]),int(mult_str[3])}
  15.                     if 0 not in mult_set and len(mult_set) == 4:
  16.                         union = set(i).union(set(j))
  17.                         if union & mult_set == set():
  18.                             total.append(mult)
  19. total_set = set(total)        
  20. print('所有乘积的和是:%d'%sum(total_set))
复制代码


结果:
所有乘积的和是:30424
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-17 20:24:12 | 显示全部楼层
有时候就盲目了,可能就标点错了也看不出来
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-18 11:14:20 | 显示全部楼层
本帖最后由 老忘 于 2016-8-18 11:34 编辑

思路:
1、要能够组合成 1 到 9 pandigital 的乘法算式,也就是说乘数、被乘数、积的位数之和必须要是9,要满足这一点,则只有1位数乘4位数、2位数乘3位数(重复的不考虑)二种情况。
2、分别从1-9中,按1-4位取出其pandigital数,存入对应列表,再按条件1进行判断。

开始采用从1到9999全部循环处理,36秒左右出结果,后改成用itertools模块取出1-4位的pandigital数再全部循环处理,14左右秒出结果,最后改成提交的这个版本,仅0.2秒左右出结果。所以在此不得不再次感叹一下,编程时的思路会影响程序效率,思路真的十分重要,要多想想啊!

  1. # -*- coding:Utf-8 -*-
  2. import itertools
  3. import time
  4. start = time.clock()

  5. def check_pd(x):#检测是否为仅含1 到 9 的pandigital数,是返回1,否返回0
  6.     for eachone in x:
  7.         if  x.count(eachone)>1 or eachone=='0':
  8.             return 0
  9.     return 1

  10. list_rel=[]
  11. for i in range(1,5):
  12.     locals()['list_'+str(i)]=[] #动态生成list_1、list_2、list_3、list_4的空列表,存放1 到 9 的分别取1-4位的pandigital数
  13.     for each in list(itertools.permutations([1,2,3,4,5,6,7,8,9],i)):
  14.         s=''
  15.         for eachone in each:
  16.             s=s+str(eachone)
  17.         locals()['list_'+str(i)].append(int(s))
  18. #要使乘数和积满足9位的pandigital数,只能是1位数乘4位数或者2位数乘3位数二种情况
  19. for i in range(1,3):
  20.     for each in locals()['list_'+str(i)]:
  21.         for eachone in locals()['list_'+str(5-i)]:
  22.             if len(str(each)+str(eachone)+str(each*eachone))==9 and  check_pd(str(each)+str(eachone)+str(each*eachone)) and (each*eachone not in list_rel):
  23.                 list_rel.append(each*eachone)

  24. print(sum(list_rel))

  25. end = time.clock()
  26. print ("read: %f s" % (end - start))
复制代码


计算结果:
  1. 45228
复制代码

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-20 08:28:13 | 显示全部楼层
  1. s = [1,2,3,4,5,6,7,8,9]

  2. def be_nums(j):#定义一个可以将组合变换为数字的函数
  3.     res = 0
  4.     for ji in j:
  5.         res = res*10 + ji
  6.     return res
  7. #可能的情况是1位乘4位等于4位
  8. #或者2位乘3位等于4位的情况
  9. product = set()
  10. import itertools
  11. #先求1位乘4位的,再求2位乘4位的
  12. for i in range(2,10):
  13.     #不可能是1乘的所以只计算2以上的乘
  14.     for j in itertools.permutations([x for x in s if x!=i],4):
  15.         temp = be_nums(j)
  16.         may_product = i*temp
  17.         if (may_product)//10000>0:
  18.             break#直接跳出内层循环?不知道有没有问题
  19.         sets = set(j)
  20.         sets.add(i)
  21.         is_break = False
  22.         while may_product>0:
  23.             te = may_product%10
  24.             if  te==0 or te in sets:
  25.                 is_break = True
  26.                 break
  27.             else:
  28.                 sets.add(te)
  29.                 may_product = may_product//10
  30.         if (not is_break) and (len(sets)==9):
  31.             product.add(i*temp)
  32. for i in itertools.permutations(s,2):
  33.     #2位乘3位的
  34.     temp1 = be_nums(i)
  35.     for j in itertools.permutations([x for x in s if x not in i],3):
  36.         temp2 = be_nums(j)
  37.         may_product = temp1*temp2
  38.         if (may_product)//10000>0:
  39.             break
  40.         sets = set(list(i)+list(j))
  41.         is_break = False
  42.         while may_product>0:
  43.             te = may_product%10
  44.             if te==0 or te in sets:
  45.                 is_break = True
  46.                 break
  47.             else:
  48.                 sets.add(te)
  49.                 may_product = may_product//10
  50.         if (not is_break) and (len(sets)==9):
  51.             product.add(temp1*temp2)
  52. print(product)
  53. print(sum(product))
复制代码


结果:{5346, 5796, 6952, 7852, 4396, 7632, 7254}
45228

评分

参与人数 1荣誉 +15 鱼币 +15 收起 理由
冬雪雪冬 + 15 + 15 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-21 17:40:30 | 显示全部楼层
  1. def func():
  2.     l1 = []
  3.     k = 0
  4.     for i in range(1000,10000):
  5.         if len(set(str(i))) == 4 and '0' not in str(i):
  6.             for j in range(1,100):
  7.                 if i%j == 0:
  8.                     if len(set(str(i)+str(j)+str(i//j))) == 9:
  9.                         l1.append(i)
  10.                         break
  11.     return l1
  12. list1 = func()
  13. print(list1,'\n',sum(list1))
复制代码

输出:
[3876, 4396, 4827, 5278, 5346, 5427, 5724, 5796, 6158, 6174, 6372, 6952, 7236, 7254, 7362, 7452, 7632, 7638, 7852, 7854, 8156, 8316, 8463]
151541
所以结果为151541
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-22 10:20:15 | 显示全部楼层
过河的小卒 发表于 2016-8-21 17:40
输出:
[3876, 4396, 4827, 5278, 5346, 5427, 5724, 5796, 6158, 6174, 6372, 6952, 7236, 7254, 7362, ...

哎,没考虑i//j得到的数中是否包含0
改进后:
  1. def func():
  2.     l1 = []
  3.     k = 0
  4.     for i in range(1000,10000):
  5.         if len(set(str(i))) == 4 and '0' not in str(i):
  6.             for j in range(1,100):
  7.                 if i%j == 0:
  8.                     if len(set(str(i)+str(j)+str(i//j))) == 9:
  9.                         if '0' not in str(i//j):
  10.                             l1.append(i)
  11.                             print(i,j,i//j)
  12.                             break
  13.     return l1
  14. list1 = func()
  15. print(list1,'\n',sum(list1))
复制代码

输出:
4396 28 157
5346 18 297
5796 12 483
6952 4 1738
7254 39 186
7632 48 159
7852 4 1963
[4396, 5346, 5796, 6952, 7254, 7632, 7852]
45228
这下对了
貌似我的代码行数最少

评分

参与人数 1荣誉 +10 鱼币 +10 收起 理由
冬雪雪冬 + 10 + 10 热爱鱼C^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-21 16:10

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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