简体   繁体   中英

Generating a random, non-prime number in python

How would I generate a non-prime random number in a range in Python?

I am confused as to how I can create an algorithm that would produce a non-prime number in a certain range. Do I define a function or create a conditional statement? I would like each number in the range to have the same probability. For example, in 1 - 100, each non-prime would not have a 1% chance but instead has a ~1.35% chance.

Now, you didn't say anything about efficiency, and this could surely be optimized, but this should solve the problem. This should be an efficient algorithm for testing primality:

import random

def isPrime(n):
    if n % 2 == 0 and n > 2: 
        return False

    return all(n % i for i in range(3, int(math.sqrt(n)) + 1, 2))

def randomNonPrime(rangeMin, rangeMax):
    nonPrimes = filter(lambda n: not isPrime(n), xrange(rangeMin, rangeMax+1))
    if not nonPrimes:
        return None

    return random.choice(nonPrimes)

minMax = (1000, 10000)
print randomNonPrime(*minMax)

After returning a list of all non-primes in range, a random value is selected from the list of non-primes, making the selection of any non-prime in range just as likely as any other non-prime in the range.

Edit

Although you didn't ask about efficiency, I was bored, so I figured out a method of doing this that makes a range of (1000, 10000000) take a little over 6 seconds on my machine instead of over a minute and a half:

import numpy
import sympy

def randomNonPrime(rangeMin, rangeMax):
    primesInRange = numpy.fromiter(
        sympy.sieve.primerange(rangeMin, rangeMax),
        dtype=numpy.uint32,
        count=-1
    )

    numbersInRange = numpy.arange(rangeMin, rangeMax+1, dtype=numpy.uint32)
    nonPrimes = numbersInRange[numpy.invert(numpy.in1d(numbersInRange, primesInRange))]

    if not nonPrimes.size:
        return None

    return numpy.random.choice(nonPrimes)

minMax = (1000, 10000000)

print randomNonPrime(*minMax)

This uses the SymPy symbolic mathematics library to optimize the generation of prime numbers in a range, and then uses NumPy to filter our output and select a random non-prime.

The algorithm and ideas to choose is very dependent on your exact use-case, as mentioned by @smarx.

Assumptions:

  • Each non-prime within the range has the same probability of beeing chosen / uniformity
  • It is sufficient that the sampled number is not a prime with a very high probability (algorithmic false positives are less likely than CPU-bugs & co.)
  • The sampling-range could be big (sieve-like approaches are slow)
  • High performance of a single sample is desired (no caching; no sampling without replacement)

Method:

  • Sample random-number in range
  • Check if this number is prime with a very fast probabilistic primality test
  • Stop when observing first non-prime number
  • If no number is found, stop algorithm after max_trials
  • max_trials -value is set by an approximation to the Coupon-Collectors-Problem ( wiki ): expected number of samples to observe each candidate once

Characteristics of method

  • Fast for single samples ( 10000 samples per second on single CPU ; given range as in example)
  • Easy to prove uniformity
  • Good asymptotic behaviour regarding range-size and range-position (number sizes)

Code

    import random
    import math

    """ Miller-Rabin primality test
            source: https://jeremykun.com/2013/06/16/miller-rabin-primality-test/
    """

    def decompose(n):
       exponentOfTwo = 0

       while n % 2 == 0:
          n = n//2  # modified for python 3!
          exponentOfTwo += 1

       return exponentOfTwo, n

    def isWitness(possibleWitness, p, exponent, remainder):
       possibleWitness = pow(possibleWitness, remainder, p)

       if possibleWitness == 1 or possibleWitness == p - 1:
          return False

       for _ in range(exponent):
          possibleWitness = pow(possibleWitness, 2, p)

          if possibleWitness == p - 1:
             return False

       return True

    def probablyPrime(p, accuracy=100):
       if p == 2 or p == 3: return True
       if p < 2: return False

       exponent, remainder = decompose(p - 1)

       for _ in range(accuracy):
          possibleWitness = random.randint(2, p - 2)
          if isWitness(possibleWitness, p, exponent, remainder):
             return False

       return True

    """ Coupon-Collector Problem (approximation)
            How many random-samplings with replacement are expected to observe each element at least once
    """
    def couponcollector(n):
        return int(n*math.log(n))

    """ Non-prime random-sampling
    """
    def get_random_nonprime(min, max):
        max_trials = couponcollector(max-min)
        for i in range(max_trials):
            candidate = random.randint(min, max)
            if not probablyPrime(candidate):
                return candidate
        return -1

    # TEST
    print(get_random_nonprime(1000, 10000000))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM