Python - Project Euler#35 - 这种方法有什么问题?

时间:2016-05-16 21:43:36

标签: python permutation primes

Euler项目的问题35如下:

  

这个数字197被称为循环素数,因为数字的所有旋转:197,971和719本身都是素数。

     

在100:2,3,5,7,11,13,17,31,37,71,73,79和97之下有十三个这样的素数。

     

在一百万以下有多少个圆形素数?

我的相当粗略的方法是首先生成低于一百万的所有素数,然后过滤掉包含偶数或数字5的所有素数(因为总会存在非素数置换)。然后,对于这个简化的素数列表中的每个元素,使用itertools模块中的permutations()函数返回所有可能的数字排列,然后检查这些排列中是否有任何不是素数,如果是,则从中删除元素素数列表。

from itertools import permutations

def gen_primes(limit):
    D = {}
    q = 2
    while q <= limit:
        if q not in D:
            yield q
            D[q * q] = [q]
        else:
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]       
        q += 1 

def odd_primes(limit):
    r = list(gen_primes(limit))
    for i in r[:]:
        for j in str(i):
            if any(int(j)%2 == 0 or int(j) == 5 for j in str(i)):
                r.remove(i)
                break
    r.extend([2,5])
    return r    

def circular_list():
    prime_list = odd_primes(1000000)
    for i in prime_list[:]:
        perm = [''.join(j) for j in permutations(str(i))]
        if any(int(j) not in prime_list for j in perm):
            prime_list.remove(i)
            break
    return prime_list

print len(circular_list)

输出产生一个不正确的值。我一直在努力在逻辑或代码(或两者)中找到错误。 permutations()是一种可行的方法吗?

我知道有更有效的方法,但如果有人能指出我使这个方法有效,我将不胜感激。

3 个答案:

答案 0 :(得分:1)

第一个问题是你需要轮换,而不是排列。可以旋转双端队列,以便我们可以替换它。第二个问题是,odd_primes()是一种速度优化,在基本代码工作之前不应该添加,因此我们暂时将其保留,并circular_list()调用{{1}直接。当然,正如您所发现的那样,素数生成器并不是最优的,因为我们必须回顾素数列表,而生成器只能迭代一次。

所以这里有什么希望使用deque和sans gen_primes()工作代码:

odd_primes()

哪个输出:

from collections import deque

def gen_primes(limit):
    D = {}
    q = 2
    while q <= limit:
        if q not in D:
            yield q
            D[q * q] = [q]
        else:
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]       
        q += 1 

def circular_list(limit):
    circular = []

    primes = list(gen_primes(limit))

    for prime in primes:
        string = str(prime)
        digits = deque(string)

        for rotation in range(1, len(string)):
            digits.rotate(1)

            if int("".join(digits)) not in primes:
                break
        else:
            circular.append(prime)

    return circular

print(circular_list(1000000))

如果这是有效的输出,现在返回并在[2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, 97, 113, 131, 197, 199, 311, 337, 373, 719, 733, 919, 971, 991, 1193, 1931, 3119, 3779, 7793, 7937, 9311, 9377, 11939, 19391, 19937, 37199, 39119, 71993, 91193, 93719, 93911, 99371, 193939, 199933, 319993, 331999, 391939, 393919, 919393, 933199, 939193, 939391, 993319, 999331] 中滑动以查看是否有任何技巧可以用来优化速度。

答案 1 :(得分:1)

使用来自cdlane的优秀代码并引入基于具有至少两位数的循环素数的速度优化仅可由数字1,3,7或9的组合组成,因为具有0,2,4,6或8作为最后一位数使得数字可以被2整除,并且将0或5作为最后一位数使其可以被5整除(来自https://en.wikipedia.org/wiki/Circular_prime):

from collections import deque
import re
import time

def gen_primes(limit):
    D = {}
    q = 2
    while q <= limit:
        if q not in D:
            yield q
            D[q * q] = [q]
        else:
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]
        q += 1

def exclude_primes(primes_list):
    regex = re.compile("[024568]")
    included_primes_list = [str(prime) for prime in primes_list if prime < 10 or regex.search(str(prime)) is None]
    return included_primes_list

def circular_list(limit):
    circular_count = 0

    primes = set(exclude_primes(gen_primes(limit)))

    for prime in primes.copy(): # need copy to process allowing update original primes list
        digits = deque(prime)

        rotations = set()
        for rotation in range(len(digits)):
            digits.rotate(1)
            rotations.add("".join(digits))
        # check all rotations at once
        if rotations.issubset(primes):
            circular_count += len(rotations)
        # remove all rotations already checked from primes list
        primes.difference_update(rotations)

    return circular_count

start_cpu_time = time.clock()
print("Number of primes:", circular_list(1000000))
end_cpu_time = time.clock()
print("CPU seconds:", end_cpu_time - start_cpu_time)

这比没有优化要快得多,但可能会进一步优化。

答案 2 :(得分:0)

这是我解决此问题的方法,大约需要1.1秒钟才能在笔记本电脑上运行...

它将预载返回列表2和5。

基本上,它会遍历3到1e6之间的所有奇数。

首先,它会运行所有数字以查看它们是否可以被2或5整除。

如果没有这样的数字,它将检查数字是否为质数。首先,它维护找到的素数列表,然后运行6k + 1算法进行测试。

对于质数,然后运行所有旋转(使用快速字符串旋转功能)以检查所有旋转是否均为质数。如果是这样,它将把该号码添加到列表中。

检查所有数字,直到1e6,它就会计算出计数和运行所花费的秒数。

它运行得足够快,我将其扩展到100,000,000(运行时间为130s),没有发现其他匹配项。

"""
Circular primes
Problem 35 
The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime.

There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97.

How many circular primes are there below one million?
"""
import time
start_time = time.time()


foundprimes = []

def isPrime(n):
    """Determine if cand is a prime number"""
    if n in foundprimes:
        return True

    if n <= 3:
        return n>1
    if n%2 == 0:
        return False
    if n%3 == 0:
        return False

    i=5
    while (i*i <= n):
        if n%i == 0:
            return False
        if n%(i+2) == 0:
            return False
        i+=6

    foundprimes.append(n)
    return True

def rotations(num):
    """Return all rotations of the given number"""

    if abs(num) < 10:
        return [num]

    numstr = str(num)
    strlen = len(numstr)
    returnarray = []
    for i in range(strlen):
        if i==0:
            pass
        else:
            start = numstr[i:]
            end = numstr[0:i]
            returnarray.append(int(start+end))


    return returnarray

def QCheck(num):
    """Do a quick check if there is an even number in the set...at some point it will be an even number in rotation"""
    numstr = str(num)
    for i in range(len(numstr)):
        if int(numstr[i]) % 2 == 0:
            return False
        if int(numstr[i]) % 5 == 0:
            return False

    return True


allrotations = [2,5]

for i in range(3,1000000,2):
    if QCheck(i):
        prime = isPrime(i)
        if prime:
            rotcheck = True
            for rot in rotations(i):
                if isPrime(rot) == False:
                    rotcheck = False

            if rotcheck:
                allrotations.append(i)

print(len(allrotations))
print("--- %s seconds ---" % (time.time() - start_time))