我正在努力解决Euler项目的以下问题,该问题简而言之就是迭代“ n”骰子并更新其值。
一排长骰子-project Euler problem #641
考虑一行n个骰子都显示1。
首先每转第二个模具(2,4,6,...),使显示的数字增加1。然后每转三个模具。现在第六个骰子将显示3。然后旋转每个第四个骰子,依此类推,直到第n个骰子(仅最后一个骰子)被翻转。如果要旋转的模具显示6,则将其更改为显示1。
让f(n)是过程结束时显示为1的骰子数。给出f(100)= 2和f(10 ^ 8)= 69。
找到f(10 ^ 36)。
我已经使用numpy在Python中编写了以下代码,但无法准确确定我在函数输出中做错了什么以匹配上面的输出。现在f(100)
返回1(应该是2);甚至f(1000)
也会返回1。
import numpy as np
def f(n):
# establish dice and the value sets for the dice
dice = np.arange(1, n + 1)
dice_values = np.ones(len(dice))
turns = range(2, len(dice) + 1)
print("{a} dice, {b} values, {c} runs to process".format(a=len(dice), b=len(dice_values), c=len(turns)))
# iterate and update the values of each die
# in our array of dice
for turn in turns:
# if die to be processed is 6, update to 1
dice_values[(dice_values == 6) & (dice % turn == 0)] = 1
# update dice_values to if the die's index has no remainder
# from the turn we're processing.
dice_values += dice % turn == 0
# output status
print('Processed every {0} dice'.format(turn))
print('{0}\n\n'.format(dice_values))
return "f({n}) = {x}".format(n=n, x=len(np.where(dice_values == 1)))
更新18/12/12
@Prune的指导非常有帮助。现在我的方法如下:
用除以6的余数为1的因子找出所有平方。
import numpy as np
# brute force to find number of factors for each n
def factors(n):
result = []
i = 1
# This will loop from 1 to int(sqrt(n))
while i * i <= n:
# Check if i divides x without leaving a remainder
if n % i == 0:
result.append(i)
if n / i != i:
result.append(n / i)
i += 1
# Return the list of factors of x
return len(result)
vect_factors = np.vectorize(factors)
# avoid brute forcing all numbers
def f(n):
# create an array of 1 to n + 1
# find all perfect squares in that range
dice = np.arange(1, n + 1)[(np.mod(np.sqrt(np.arange(1, n + 1)), 1) == 0)]
# find all squares which have n-factors, which
# when divided by 6 have a remainder of 1.
dice = dice[np.mod(vect_factors(dice), 6) == 1]
return len(dice)
值得注意的是-在我的机器上,我无法运行大于10 ^ 10的值。虽然解决这个问题是理想的,但我觉得我在过程中学到的(并确定如何应用)对我来说已经足够。
更新2018年11月13日
我将继续花费少量时间来尝试优化它,以使其更快地处理。这是更新的代码库。这将在1分17秒内计算出f(10 ** 10)。
import time
from datetime import timedelta
import numpy as np
import math
from itertools import chain, cycle, accumulate
def find_squares(n):
return np.array([n ** 2 for n in np.arange(1, highest = math.sqrt(n) + 1)])
# brute force to find number of factors for each n
def find_factors(n):
def prime_powers(n):
# c goes through 2, 3, 5, then the infinite (6n+1, 6n+5) series
for c in accumulate(chain([2, 1, 2], cycle([2, 4]))):
if c * c > n: break
if n % c: continue
d, p = (), c
while not n % c:
n, p, d = n // c, p * c, d + (p,)
yield (d)
if n > 1: yield ((n,))
r = [1]
for e in prime_powers(n):
r += [a * b for a in r for b in e]
return len(r)
vect_factors = np.vectorize(find_factors)
# avoid brute forcing all numbers
def f(n):
# create an array of 1 to n + 1
# find all perfect squares in that range
start = time.time()
dice = find_squares(n)
# find all squares which have n-factors, which
# when divided by 6 have a remainder of 1.
dice = dice[np.mod(vect_factors(dice), 6) == 1]
diff = (timedelta(seconds=int(time.time() - start))).__str__()
print("{n} has {remain} dice with a value of 1. Computed in {diff}.".format(n=n, remain=len(dice), diff=diff))
答案 0 :(得分:5)
我要提出一个 x / y问题。修复6 => 1翻转将纠正您的代码,但不能在合理的时间内解决出现的问题。要查找f(10^36)
,您每次要处理10 ^ 36个骰子10 ^ 36次,即使这只是过滤器中的除数检查。总共有10 ^ 72张支票。我不知道您拥有什么硬件,但是即使我的多核怪兽也无法足够快地循环10 ^ 72次来获得舒适感。
相反,您需要找出潜在的问题,并尝试生成一个符合描述的整数计数。
骰子仅是对mod 6中的事物进行计数的设备。我们正在对数字的除数进行计数,包括1和数字本身。这就是(著名的)divisor function。
眼前的问题并不要求我们为所有数字求出σ0(n);而是它希望我们计数多少个整数的σ0(n)= 1(模6)。这些数字是1、7、13、19,...除数。
首先,请注意,这是一个 odd 编号。唯一具有奇数个除数的整数是完美平方。看除数函数;我们如何判断一个数的 square 是否将具有所需数量的因子1(模6)?
这会让你动起来吗?
周末更新
在本日历年度中,我要逐步完成10 ^ 18个候选人的代码仍然太慢,无法完成。效果很好,直到大约10 ^ 7,然后在 O(N log N)检查步骤中陷入困境。
但是,我在跟踪输出中注意到的更多限制。 主要的特点是确定素数组合会导致什么解决方案。如果减少每个功率模3,我们将得到以下结果:
0
值不会影响结果的有效性。1
值使数字无效。2
值必须成对。此外,这些条件对于将给定数字声明为解决方案既必要又充分。因此,有可能生成所需的解,而无需费心地通过所有整数<= 10 ^ 18的平方。
除其他事项外,我们仅需要质数最大为10 ^ 9的质数:解的平方根将需要至少2个质数因子。
我希望现在有足够的提示...您将需要构造一种算法来生成具有给定上限的某些受限复合组合。
答案 1 :(得分:1)
正如Thierry在评论中提到的那样,当您将骰子以6翻转时,您将循环回到2。我建议您将dice_values[(dice_values == 6) & (dice % turn == 0)] = 1
更改为等于0。
您还遇到了return "f({n}) = {x}".format(n=n, x=len(np.where(dice_values == 1)))
的问题,可以通过将x=len(np.where(dice_values == 1))
替换为x=np.count_nonzero(dice_values == 1)
进行这两项更改后,我的输出为f(100)=2