欧拉专案#641 Python 3.6-Numpy

时间:2018-11-09 21:30:16

标签: python numpy

我正在努力解决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的指导非常有帮助。现在我的方法如下:

  • 找到从1到n的所有平方。
  • 用除以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))

2 个答案:

答案 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