如何才能找到哪一个更快速搜索素数的大文件或逐个检查数字的素数?

时间:2015-10-11 20:29:37

标签: python algorithm matlab

我在MATLAB中编写了一些代码,用isprime()函数搜索素数模式,但现在我需要在python中做同样的事情。但是,在python中没有内置isprime()函数,现在我想知道哪一个更快:

1)搜索素数的大文件,找出哪个数字是素数。

2)编写一个计算素数的函数。

3 个答案:

答案 0 :(得分:1)

你应该测试一下,然后让你知道会发生什么:

Time complexity of Sieve of Eratosthenes algorithm(使用维基百科)指出,Eratosthenes算法的Sieve具有运行时O(n(logn)(loglogn))。从磁盘读取(假设某人已经完成了数学运算)将是O(n)操作;在这两种情况下,你应该能够将素数存储在python dict中,然后检查数字是否为素数是O(1)。所以,一般来说(假设你可以得到素数列表),从磁盘读取的速度会更快。

请注意,有些算法可能会在较少的操作中无法确定地确定一个数字是否为素数(但听起来你会为了寻找你的模式而调用很多)。这就是加密使用的(例如Miller-Rabin)

答案 1 :(得分:1)

这个问题的答案很大程度上取决于你愿意为这两种方法投入多少记忆,你希望搜索的素数范围以及你如何计算/测试你的素数列表。

为了便于比较,我将提供两种方法;

方法A接受预先生成的第一百万个素数列表; (使用找到here的素数列表,从文件中删除引入;你可以通过使文件的格式更容易解析来提高效率,但我不打扰)

方法B使用素数测试的组合,sieve of eratosthenes的一个子集,使用前250个素数来快速消除复合数,fermat primality test增加候选素数的概率确实素数,最后跟着miller-rabin primality test

方法A:使用预先计算的素数列表

import time, re, random

methodAStartTime = time.time()
with open('primes1.txt') as primeFile:
    # Constructs a set of all primes in the file
    primes = {int(prime) for prime in re.findall("\S+", primeFile.read())}

    # Choose 250 random numbers to look up
    # 15485863 is the final prime in the file, and hence the upper limit of our test range
    toTest = [random.randrange(0, 15485863) for i in range(250)]
    foundPrimes = []

    methodATestStartTime = time.time()
    for candidate in toTest:
        if candidate in primes:
            foundPrimes.append(candidate)

    methodAStopTime = time.time()
    print foundPrimes
    print "Method A took %s seconds to construct the list of 1 million primes" % (methodAStopTime - methodAStartTime)
    print "Method A took %s seconds to do 250 lookups" % (methodAStopTime - methodATestStartTime)

结果:

$ python fileLookup.py
[675781, 4715407, 4440523, 14153297, 9673057, 13331299, 4195357, 7219307, 14390969, 1340237, 2875823, 13440881, 954677, 12005717, 11477101, 3783629, 7046069, 5098969, 11821031]
Method A took 1.08314299583 seconds to construct the list of 1 million primes
Method A took 9.79900360107e-05 seconds to do 250 lookups


$ python fileLookup.py
[3051749, 15035039, 7111123, 6855491, 5722303, 9594217, 14680751, 2542607, 4171003, 5353993, 1068131, 1637177, 195893, 617269, 2951497, 5937227, 14768029, 9201733, 4898497, 10333123]
Method A took 1.08329796791 seconds to construct the list of 1 million primes
Method A took 9.70363616943e-05 seconds to do 250 lookups

方法A的优点是,一旦你构建了素数列表并将它们输入到python集对象中,你就可以在O(1)时间内获得令人难以置信的快速保证精度素数测试;缺点是你首先必须构建你的素数列表,你只能对一定范围的候选数字进行素数测试。

但是,如果您希望能够测试任意大的素数,那么最好的方法是使用启发式方法。下面是一些代码,它取一个给定的数字并测试它的原始性(尽管我相当确定它有效,我会犹豫是否在没有彻底测试它的情况下在其他地方使用我的代码)

方法B:使用启发式素性测试

import random, time

# Maximum number of checks to make when running the fermat primality test
FERMAT_ITERATIONS = 16

# Maximum number of checks to make when running the miller rabin primality test
MILLER_RABIN_ITERATIONS = 32

# List of the first 256 prime numbers for quickly eliminating composite numbers
SIEVE_PRIMES = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
                103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
                211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
                331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
                449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
                587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
                709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
                853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
                991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093,
                1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
                1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327,
                1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481,
                1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597,
                1601, 1607, 1609, 1613, 1619)


def _subsetSievePrimalityTest(n):
    """
    Tests the divisibility of n using the first SIEVE_LENGTH prime numbers.

    Inputs: n - The value to test for primality, using SIEVE_PRIMES.

    Returns True if n is deemed to be a candidate prime.
    """

    for prime in SIEVE_PRIMES:
        if not n % prime and n != prime:
            return False
    return True


def _millerRabinPrimalityTest(n):
    """
    Runs the Miller-Rabin primality test to assert within probable means that n is a prime number.

    Inputs: n: The number to test for primality

    Note: Assumes n > 4.

    Returns True if n is a candidate prime, else False.
    """

    r = n - 1
    s = 0

    while not r & 1:
        r /= 2
        s += 1

    if not r:
        return False

    for i in range(MILLER_RABIN_ITERATIONS):
        randA = random.randint(2, n - 2)
        if pow(randA, r, n) != 1:
            for j in range(s + 1):
                if pow(randA, pow(2, j) * r, n) == n - 1:
                    break
            else:
                return False

    return True


def _fermatPrimalityTest(n):
    """
    Runs the Fermat Primality test to quickly discard non-prime candidates.

    Inputs: n: The number to test for primality

    Note: Assumes n > 2

    Returns True if n is a candidate prime, else False.
    """

    for i in range(FERMAT_ITERATIONS):
        randA = random.randint(2, n - 1)
        if pow(randA, n - 1, n) != 1:
            return False

    return True


def _testPrime(n):
    """ Tests if n is a prime number, to a high probability of accuracy. """

    # Run the subsetSievePrimalityTest to quickly eliminate most candidates
    if not _subsetSievePrimalityTest(n):
        return False

    # Run the fermat primality test to increase the probability that n is a prime
    if not _fermatPrimalityTest(n):
        return False

    # Run the miller-rabin primality test to increase the liklihood that n is a prime number.
    if not _millerRabinPrimalityTest(n):
        return False

    # If all the primality tests return True, n is very likely a prime.
    return True

# Perform 250 primality tests
primes = []
before = time.time()
for i in range(250):
    n = random.randrange(5, 10000000000000000000000000000000000000000000000000000000000000000000000)
    if _testPrime(n):
        primes.append(n)
print primes
print "250 tests took %s seconds." % (time.time() - before)

结果:

$ python primes.py
[1397851093443935893893199704415888634420504940995831429393995086101779L, 7170601962705551861928144990317179138094235542914256028587854974779083L]
250 tests took 0.0264971256256 seconds.

$ python primes.py
[8357905131314475918050404529147549250259829943945760080686207992488773L, 9373617169959281562405528688440072510316310402630308017260952694184939L, 5558837883632138346976786452679446510211389690872968737719419795450487L]
250 tests took 0.0443639755249 seconds.

方法B的优点是,您可以随时测试任意数量的任何数量而无需事先做任何工作;缺点是它在技术上可能的复合数字可能被错误地归类为素数,并且每个单独的查找比方法A要长得多。

在一天结束时,如果您知道要查找的素数范围并且打算进行多次查找,则方法A会更好。如果你想对任意数字进行测试,或者你想要测试的数字范围非常非常大,并且你希望节省磁盘空间和内存,方法B才会更好。

答案 2 :(得分:0)

首先:检查你是否真的需要快速算法!也许你使用的数字足够小,以至于与使用Eratosthenes的Sieve相比,时差不会太明显。

如果情况并非如此,那么基本上有三种情况:

  • 如果文件中有足够小的列表,您可以阅读一次 并且完全留在记忆中,你要去O(1)时间。
  • 如果您在文件中有一个很长的列表并且无法将其保留在内存中,则您必须二进制搜索该文件以及该O(log(n))时间
  • 如果你没有任何清单,那么......花点时间,因为对于大数字来说会很慢: - )

也许你可以做一些事情,比如在内存中保留足够小的列表。如果数字太大,请检查文件。如果它'仍然太大,使用素性测试来确定它是否是素数。

之后,将可能新找到的素数保存在列表中以供将来参考。

如果它不是素数,也许还可以保存它,否则对于大数字你不得不重新做一遍,只是为了找到一个负面结果。