ฉันจะสร้างรายการหมายเลขสุ่มโดยไม่ซ้ำกันได้อย่างไร


113

ฉันพยายามใช้random.randint(0, 100)แต่ตัวเลขบางตัวก็เหมือนกัน มีวิธีการ / โมดูลในการสร้างรายการตัวเลขสุ่มเฉพาะหรือไม่?

หมายเหตุ: รหัสต่อไปนี้อ้างอิงจากคำตอบและได้รับการเพิ่มหลังจากโพสต์คำตอบแล้ว ไม่ใช่ส่วนหนึ่งของคำถาม มันคือทางออก

def getScores():
    # open files to read and write
    f1 = open("page.txt", "r");
    p1 = open("pgRes.txt", "a");

    gScores = [];
    bScores = [];
    yScores = [];

    # run 50 tests of 40 random queries to implement "bootstrapping" method 
    for i in range(50):
        # get 40 random queries from the 50
        lines = random.sample(f1.readlines(), 40);

1
หากไม่ซ้ำกันก็สามารถสุ่มได้อย่างแท้จริงในบริบทที่ถูกต้อง เช่นเดียวกับการสุ่มตัวอย่างของดัชนีโดยไม่มีการแทนที่ก็ยังสามารถสุ่มได้อย่างสมบูรณ์
gbtimmon

คำตอบ:


185

สิ่งนี้จะส่งคืนรายการ 10 หมายเลขที่เลือกจากช่วง 0 ถึง 99 โดยไม่ซ้ำกัน

import random
random.sample(range(100), 10)

ด้วยการอ้างอิงถึงตัวอย่างโค้ดเฉพาะของคุณคุณอาจต้องการอ่านบรรทัดทั้งหมดจากไฟล์หนึ่งครั้งจากนั้นเลือกบรรทัดแบบสุ่มจากรายการที่บันทึกไว้ในหน่วยความจำ ตัวอย่างเช่น:

all_lines = f1.readlines()
for i in range(50):
    lines = random.sample(all_lines, 40)

วิธีนี้คุณจะต้องอ่านจากไฟล์จริง ๆ เพียงครั้งเดียวก่อนที่จะวนซ้ำ การทำเช่นนี้มีประสิทธิภาพมากกว่าการย้อนกลับไปที่จุดเริ่มต้นของไฟล์และเรียกf1.readlines()อีกครั้งสำหรับการวนซ้ำแต่ละครั้ง


3
เทคนิคนี้ทำให้สิ้นเปลืองหน่วยความจำโดยเฉพาะอย่างยิ่งสำหรับตัวอย่างขนาดใหญ่ ฉันโพสต์โค้ดสำหรับหน่วยความจำที่มากขึ้นและคำนวณโซลูชันที่มีประสิทธิภาพด้านล่างซึ่งใช้ Linear Congruential Generator
Thomas Lux

ฉันชี้ให้เห็นว่าวิธี LCG นั้น "สุ่ม" น้อยกว่าดังนั้นหากคุณต้องการสร้างลำดับสุ่มที่ไม่ซ้ำกันจำนวนมากความหลากหลายจะน้อยกว่าโซลูชันนี้ หากคุณต้องการลำดับสุ่มเพียงหยิบมือ LCG คือหนทางที่จะไป!
Thomas Lux

ขอบคุณ Greg มันมีประโยชน์
N Sivaram

numpyแทนที่จะrandom ดูเหมือนเร็วกว่า import numpy as np; np.random.permutation(100)[:10]ยังสร้าง 10 หมายเลขที่เลือกจาก 0 ถึง 99 โดยไม่ซ้ำกัน Benchmarking ใน IPython ให้ผล 103 s ± 513 ns สำหรับ%timeit random.sample(range(1000), 100) และ 17 µs ± 1.24 µs สำหรับ%timeit np.random.permutation(1000)[:100] .
Ant Plante

15

คุณสามารถใช้ฟังก์ชันสุ่มจากโมดูลสุ่มดังนี้:

import random

my_list = list(xrange(1,100)) # list of integers from 1 to 99
                              # adjust this boundaries to fit your needs
random.shuffle(my_list)
print my_list # <- List of unique random numbers

โปรดทราบว่าวิธีการสุ่มจะไม่ส่งคืนรายการใด ๆ ตามที่คาดไว้ แต่จะสลับรายการที่ส่งผ่านโดยการอ้างอิงเท่านั้น


เป็นการดีที่จะพูดถึงที่นี่ว่า xrange ใช้งานได้เฉพาะใน Python 2 และไม่ใช้ใน Python 3
Shayan Shafiq

10

ก่อนอื่นคุณสามารถสร้างรายการตัวเลขจากaไปยังbที่ใดaและbเป็นตัวเลขที่น้อยที่สุดและมากที่สุดในรายการของคุณก่อนจากนั้นสับเปลี่ยนด้วยอัลกอริทึมFisher-Yatesหรือใช้random.shuffleวิธีของ Python


1
การสร้างรายการดัชนีทั้งหมดเป็นการสิ้นเปลืองหน่วยความจำโดยเฉพาะอย่างยิ่งสำหรับตัวอย่างขนาดใหญ่ ฉันโพสต์โค้ดสำหรับหน่วยความจำที่มากขึ้นและคำนวณโซลูชันที่มีประสิทธิภาพด้านล่างซึ่งใช้ Linear Congruential Generator
Thomas Lux

8

คำตอบที่นำเสนอในคำตอบนี้ใช้ได้ผล แต่อาจเป็นปัญหากับหน่วยความจำหากขนาดของกลุ่มตัวอย่างมีขนาดเล็ก แต่ประชากรมีขนาดใหญ่ (เช่นrandom.sample(insanelyLargeNumber, 10))

เพื่อแก้ไขปัญหานี้ฉันจะไปกับสิ่งนี้:

answer = set()
sampleSize = 10
answerSize = 0

while answerSize < sampleSize:
    r = random.randint(0,100)
    if r not in answer:
        answerSize += 1
        answer.add(r)

# answer now contains 10 unique, random integers from 0.. 100

ตอนนี้random.sampleใช้แนวทางนี้กับกลุ่มตัวอย่างจำนวนน้อยจากประชากรจำนวนมากดังนั้นปัญหาเกี่ยวกับหน่วยความจำจึงไม่มีอยู่จริงอีกต่อไป แม้ว่าในขณะที่เขียนคำตอบนี้การใช้งานrandom.shuffleอาจแตกต่างออกไป
kyrill

5

Linear Congruential Pseudo-random Number Generator

O (1) หน่วยความจำ

O (k) การดำเนินงาน

ปัญหานี้สามารถแก้ไขได้ด้วยLinear Congruential Generatorอย่างง่าย สิ่งนี้ต้องการค่าโสหุ้ยของหน่วยความจำคงที่ (จำนวนเต็ม 8 ตัว) และการคำนวณสูงสุด 2 * (ความยาวของลำดับ)

โซลูชันอื่น ๆ ทั้งหมดใช้หน่วยความจำมากกว่าและประมวลผลได้มากขึ้น! หากคุณต้องการเพียงไม่กี่ลำดับแบบสุ่มวิธีนี้จะถูกกว่ามาก สำหรับช่วงของขนาดNหากคุณต้องการสร้างตามลำดับของลำดับที่Nไม่ซ้ำกันkหรือมากกว่านั้นฉันขอแนะนำวิธีแก้ปัญหาที่ยอมรับโดยใช้วิธีการในตัวrandom.sample(range(N),k)เนื่องจากได้รับการปรับให้เหมาะสมใน python เพื่อความเร็ว

รหัส

# Return a randomized "range" using a Linear Congruential Generator
# to produce the number sequence. Parameters are the same as for 
# python builtin "range".
#   Memory  -- storage for 8 integers, regardless of parameters.
#   Compute -- at most 2*"maximum" steps required to generate sequence.
#
def random_range(start, stop=None, step=None):
    import random, math
    # Set a default values the same way "range" does.
    if (stop == None): start, stop = 0, start
    if (step == None): step = 1
    # Use a mapping to convert a standard range into the desired range.
    mapping = lambda i: (i*step) + start
    # Compute the number of numbers in this range.
    maximum = (stop - start) // step
    # Seed range with a random integer.
    value = random.randint(0,maximum)
    # 
    # Construct an offset, multiplier, and modulus for a linear
    # congruential generator. These generators are cyclic and
    # non-repeating when they maintain the properties:
    # 
    #   1) "modulus" and "offset" are relatively prime.
    #   2) ["multiplier" - 1] is divisible by all prime factors of "modulus".
    #   3) ["multiplier" - 1] is divisible by 4 if "modulus" is divisible by 4.
    # 
    offset = random.randint(0,maximum) * 2 + 1      # Pick a random odd-valued offset.
    multiplier = 4*(maximum//4) + 1                 # Pick a multiplier 1 greater than a multiple of 4.
    modulus = int(2**math.ceil(math.log2(maximum))) # Pick a modulus just big enough to generate all numbers (power of 2).
    # Track how many random numbers have been returned.
    found = 0
    while found < maximum:
        # If this is a valid value, yield it in generator fashion.
        if value < maximum:
            found += 1
            yield mapping(value)
        # Calculate the next value in the sequence.
        value = (value*multiplier + offset) % modulus

การใช้งาน

การใช้ฟังก์ชัน "random_range" นี้จะเหมือนกับตัวสร้างใด ๆ (เช่น "range") ตัวอย่าง:

# Show off random range.
print()
for v in range(3,6):
    v = 2**v
    l = list(random_range(v))
    print("Need",v,"found",len(set(l)),"(min,max)",(min(l),max(l)))
    print("",l)
    print()

ตัวอย่างผลลัพธ์

Required 8 cycles to generate a sequence of 8 values.
Need 8 found 8 (min,max) (0, 7)
 [1, 0, 7, 6, 5, 4, 3, 2]

Required 16 cycles to generate a sequence of 9 values.
Need 9 found 9 (min,max) (0, 8)
 [3, 5, 8, 7, 2, 6, 0, 1, 4]

Required 16 cycles to generate a sequence of 16 values.
Need 16 found 16 (min,max) (0, 15)
 [5, 14, 11, 8, 3, 2, 13, 1, 0, 6, 9, 4, 7, 12, 10, 15]

Required 32 cycles to generate a sequence of 17 values.
Need 17 found 17 (min,max) (0, 16)
 [12, 6, 16, 15, 10, 3, 14, 5, 11, 13, 0, 1, 4, 8, 7, 2, ...]

Required 32 cycles to generate a sequence of 32 values.
Need 32 found 32 (min,max) (0, 31)
 [19, 15, 1, 6, 10, 7, 0, 28, 23, 24, 31, 17, 22, 20, 9, ...]

Required 64 cycles to generate a sequence of 33 values.
Need 33 found 33 (min,max) (0, 32)
 [11, 13, 0, 8, 2, 9, 27, 6, 29, 16, 15, 10, 3, 14, 5, 24, ...]

1
นี่มันเจ๋งมาก! แต่ฉันมั่นใจว่ามันตอบคำถามได้จริงๆ บอกว่าฉันต้องการสุ่มตัวอย่าง 2 ค่าจาก 0 ถึง 4 โดยไม่สร้างค่าของตัวเองprimeฟังก์ชันจะส่งคืนคำตอบที่เป็นไปได้เพียง 4 คำตอบเท่านั้นเนื่องจากvalueเป็นสิ่งเดียวที่สุ่มเลือกโดยมีค่าที่เป็นไปได้ 4 ค่าเมื่อเราต้องการอย่างน้อย (4 เลือก 2) = 6, (อนุญาตให้มีการสั่งซื้อแบบไม่สุ่ม) random_range(2,4)จะคืนค่า {(1, 0), (3, 2), (2, 1), (0, 3)} แต่จะไม่คืนค่าคู่ (3,1) (หรือ (1,3)) คุณคาดหวังว่าจะมีการเรียกใช้ฟังก์ชันครั้งใหญ่แบบสุ่มครั้งใหม่หรือไม่
wowserx

1
(นอกจากนี้ฉันสมมติว่าคุณคาดหวังให้ผู้คนสับเปลี่ยนลำดับหลังจากที่ฟังก์ชันของคุณส่งคืนหากพวกเขาต้องการการจัดลำดับแบบสุ่มเนื่องจากrandom_range(v)จะส่งกลับไปยังvลำดับที่ไม่ซ้ำกันแทนv!)
wowserx

จริงทั้งหมด! เป็นการยากที่จะสร้างสมดุลระหว่างการหลีกเลี่ยงการล้นจำนวนเต็มและการสร้างลำดับแบบสุ่มที่เพียงพอ ฉันอัปเดตฟังก์ชันเพื่อรวมการสุ่มเพิ่มขึ้นเล็กน้อย แต่ก็ยังไม่สุ่มเท่า v !. ขึ้นอยู่กับว่าคุณต้องการใช้ฟังก์ชันหลายครั้งหรือไม่ วิธีนี้เหมาะที่สุดเมื่อคุณสร้างจากค่าที่หลากหลาย (เมื่อการใช้หน่วยความจำของผู้อื่นจะสูงขึ้นมาก) ฉันจะคิดถึงมันมากขึ้นขอบคุณ!
Thomas Lux

4

หากรายการของหมายเลข N ตั้งแต่ 1 ถึง N ถูกสร้างขึ้นแบบสุ่มใช่มีความเป็นไปได้ที่ตัวเลขบางตัวอาจซ้ำกัน

หากคุณต้องการรายการหมายเลขตั้งแต่ 1 ถึง N ในลำดับที่สุ่มกรอกอาร์เรย์กับจำนวนเต็มตั้งแต่ 1 ถึง N และจากนั้นใช้สับเปลี่ยน Fisher-Yatesrandom.shuffle()หรืองูใหญ่


3

หากคุณต้องการสุ่มตัวอย่างจำนวนมากคุณไม่สามารถใช้ range

random.sample(range(10000000000000000000000000000000), 10)

เพราะมันพ่น:

OverflowError: Python int too large to convert to C ssize_t

นอกจากนี้หากrandom.sampleไม่สามารถผลิตสินค้าได้ตามจำนวนที่คุณต้องการเนื่องจากช่วงมีขนาดเล็กเกินไป

 random.sample(range(2), 1000)

มันพ่น:

 ValueError: Sample larger than population

ฟังก์ชันนี้ช่วยแก้ปัญหาทั้งสองประการ:

import random

def random_sample(count, start, stop, step=1):
    def gen_random():
        while True:
            yield random.randrange(start, stop, step)

    def gen_n_unique(source, n):
        seen = set()
        seenadd = seen.add
        for i in (i for i in source() if i not in seen and not seenadd(i)):
            yield i
            if len(seen) == n:
                break

    return [i for i in gen_n_unique(gen_random,
                                    min(count, int(abs(stop - start) / abs(step))))]

การใช้งานที่มีตัวเลขมาก:

print('\n'.join(map(str, random_sample(10, 2, 10000000000000000000000000000000))))

ตัวอย่างผลลัพธ์:

7822019936001013053229712669368
6289033704329783896566642145909
2473484300603494430244265004275
5842266362922067540967510912174
6775107889200427514968714189847
9674137095837778645652621150351
9969632214348349234653730196586
1397846105816635294077965449171
3911263633583030536971422042360
9864578596169364050929858013943

การใช้งานที่ช่วงมีขนาดเล็กกว่าจำนวนรายการที่ร้องขอ:

print(', '.join(map(str, random_sample(100000, 0, 3))))

ตัวอย่างผลลัพธ์:

2, 0, 1

นอกจากนี้ยังใช้งานได้กับช่วงและขั้นตอนเชิงลบ:

print(', '.join(map(str, random_sample(10, 10, -10, -2))))
print(', '.join(map(str, random_sample(10, 5, -5, -2))))

ตัวอย่างผลลัพธ์:

2, -8, 6, -2, -4, 0, 4, 10, -6, 8
-3, 1, 5, -1, 3

จะเกิดอะไรขึ้นถ้าคุณสร้างตัวเลขมากกว่า 8 พันล้านหมายเลขไม่ช้าก็เร็วสิ่งที่เห็นจะกลายเป็นเรื่องใหญ่เกินไป
david_adler

คำตอบนี้มีข้อบกพร่องอย่างรุนแรงสำหรับกลุ่มตัวอย่างขนาดใหญ่ ความน่าจะเป็นของการชนกันจะเพิ่มขึ้นเป็นเชิงเส้นในแต่ละขั้นตอน ฉันโพสต์วิธีแก้ปัญหาโดยใช้ Linear Congruential Generator ที่มี O (1) หน่วยความจำเหนือศีรษะและ O (k) ขั้นตอนที่จำเป็นสำหรับการสร้างตัวเลข k สิ่งนี้สามารถแก้ไขได้อย่างมีประสิทธิภาพมากขึ้น!
Thomas Lux

คำตอบนี้ดีกว่าอย่างแน่นอนหากคุณต้องการสร้างลำดับสุ่มจำนวนหนึ่งตามลำดับความยาวของลำดับ! วิธี LCG นั้น "สุ่ม" น้อยกว่าเมื่อต้องสร้างลำดับที่ไม่ซ้ำกันหลาย ๆ
Thomas Lux

"ฟังก์ชันนี้แก้ไขปัญหาทั้งสอง"ได้อย่างไร? คุณยังไม่สามารถรับตัวอย่าง 1,000 ตัวอย่างจากประชากร 2 คนแทนที่จะโยนข้อยกเว้นคุณให้ผลลัพธ์ที่ไม่ถูกต้อง นั่นแทบจะไม่สามารถแก้ปัญหาของ "ปัญหา" ได้ (ซึ่งจริงๆแล้วไม่ใช่ปัญหาในการเริ่มต้นเนื่องจากไม่สมเหตุสมผลเลยที่จะขอkตัวอย่างเฉพาะจากประชากรn <k )
kyrill

1

คุณสามารถใช้Numpy library เพื่อรับคำตอบอย่างรวดเร็วดังที่แสดงด้านล่าง -

ข้อมูลโค้ดที่ระบุจะแสดงหมายเลขที่ไม่ซ้ำกัน 6 หมายเลขระหว่างช่วง 0 ถึง 5 คุณสามารถปรับพารามิเตอร์เพื่อความสะดวกสบายของคุณ

import numpy as np
import random
a = np.linspace( 0, 5, 6 )
random.shuffle(a)
print(a)

เอาต์พุต

[ 2.  1.  5.  3.  4.  0.]

มันไม่ได้วางข้อ จำกัด ใด ๆ ตามที่เราเห็นใน random.sample ตามที่อ้างนี่

หวังว่านี่จะช่วยได้เล็กน้อย


1

ฟังก์ชันง่ายๆที่ช่วยแก้ปัญหาของคุณได้

from random import randint

data = []

def unique_rand(inicial, limit, total):

        data = []

        i = 0

        while i < total:
            number = randint(inicial, limit)
            if number not in data:
                data.append(number)
                i += 1

        return data


data = unique_rand(1, 60, 6)

print(data)


"""

prints something like 

[34, 45, 2, 36, 25, 32]

"""

1

คำตอบที่ให้ไว้ที่นี่ใช้งานได้ดีทั้งในด้านเวลาและหน่วยความจำ แต่ซับซ้อนกว่าเล็กน้อยเนื่องจากใช้โครงสร้าง python ขั้นสูงเช่นอัตราผลตอบแทน คำตอบง่ายทำงานได้ดีในทางปฏิบัติ แต่ปัญหากับคำตอบที่ว่าก็คือว่ามันอาจสร้างจำนวนเต็มปลอมจำนวนมากก่อนที่จริงการสร้างชุดที่จำเป็น ลองใช้งานกับประชากรขนาด = 1000, sampleSize = 999 ในทางทฤษฎีมีโอกาสที่จะไม่ยุติ

คำตอบด้านล่างกล่าวถึงปัญหาทั้งสองเนื่องจากเป็นปัจจัยที่กำหนดได้และค่อนข้างมีประสิทธิภาพแม้ว่าในปัจจุบันจะไม่ได้มีประสิทธิภาพเท่าอีกสองข้อ

def randomSample(populationSize, sampleSize):
  populationStr = str(populationSize)
  dTree, samples = {}, []
  for i in range(sampleSize):
    val, dTree = getElem(populationStr, dTree, '')
    samples.append(int(val))
  return samples, dTree

โดยที่ฟังก์ชัน getElem, percolateUp เป็นไปตามที่กำหนดไว้ด้านล่าง

import random

def getElem(populationStr, dTree, key):
  msd  = int(populationStr[0])
  if not key in dTree.keys():
    dTree[key] = range(msd + 1)
  idx = random.randint(0, len(dTree[key]) - 1)
  key = key +  str(dTree[key][idx])
  if len(populationStr) == 1:
    dTree[key[:-1]].pop(idx)
    return key, (percolateUp(dTree, key[:-1]))
  newPopulation = populationStr[1:]
  if int(key[-1]) != msd:
    newPopulation = str(10**(len(newPopulation)) - 1)
  return getElem(newPopulation, dTree, key)

def percolateUp(dTree, key):
  while (dTree[key] == []):
    dTree[key[:-1]].remove( int(key[-1]) )
    key = key[:-1]
  return dTree

สุดท้ายเวลาโดยเฉลี่ยอยู่ที่ประมาณ 15 มิลลิวินาทีสำหรับค่าขนาดใหญ่ของ n ดังที่แสดงด้านล่าง

In [3]: n = 10000000000000000000000000000000

In [4]: %time l,t = randomSample(n, 5)
Wall time: 15 ms

In [5]: l
Out[5]:
[10000000000000000000000000000000L,
 5731058186417515132221063394952L,
 85813091721736310254927217189L,
 6349042316505875821781301073204L,
 2356846126709988590164624736328L]

คุณคิดว่าคำตอบนั้นซับซ้อนหรือไม่? นี่มันอะไรกัน?! จากนั้นก็มีคำตอบอื่นซึ่งสร้าง "จำนวนเต็มปลอม" จำนวนมาก ฉันดำเนินการใช้งานของคุณด้วยอินพุตตัวอย่างที่คุณให้ (ประชากรขนาด = 1000, sampleSize = 999) เวอร์ชันของคุณเรียกใช้random.randintฟังก์ชัน 3996 ครั้งในขณะที่อีกเวอร์ชันหนึ่งเรียกใช้ฟังก์ชัน cca 6000 ครั้ง ไม่ใช่การปรับปรุงที่ใหญ่โตเหรอ?
kyrill

@kyrill คุณใช้คำตอบนี้
aak318

1

เพื่อให้ได้โปรแกรมที่สร้างรายการของค่าสุ่มโดยไม่มีรายการซ้ำที่กำหนดมีประสิทธิภาพและสร้างขึ้นด้วยโครงสร้างการเขียนโปรแกรมขั้นพื้นฐานให้พิจารณาฟังก์ชันที่extractSamplesกำหนดไว้ด้านล่าง

def extractSamples(populationSize, sampleSize, intervalLst) :
    import random
    if (sampleSize > populationSize) :
        raise ValueError("sampleSize = "+str(sampleSize) +" > populationSize (= " + str(populationSize) + ")")
    samples = []
    while (len(samples) < sampleSize) :
        i = random.randint(0, (len(intervalLst)-1))
        (a,b) = intervalLst[i]
        sample = random.randint(a,b)
        if (a==b) :
            intervalLst.pop(i)
        elif (a == sample) : # shorten beginning of interval                                                                                                                                           
            intervalLst[i] = (sample+1, b)
        elif ( sample == b) : # shorten interval end                                                                                                                                                   
            intervalLst[i] = (a, sample - 1)
        else :
            intervalLst[i] = (a, sample - 1)
            intervalLst.append((sample+1, b))
        samples.append(sample)
    return samples

แนวคิดพื้นฐานคือการติดตามช่วงเวลาintervalLstสำหรับค่าที่เป็นไปได้ที่จะเลือกองค์ประกอบที่เราต้องการ นี่เป็นปัจจัยกำหนดในแง่ที่เรารับประกันว่าจะสร้างตัวอย่างภายในจำนวนขั้นตอนที่กำหนด (ขึ้นอยู่กับpopulationSizeและsampleSize)

ในการใช้ฟังก์ชันด้านบนเพื่อสร้างรายการที่เราต้องการ

In [3]: populationSize, sampleSize = 10**17, 10**5

In [4]: %time lst1 = extractSamples(populationSize, sampleSize, [(0, populationSize-1)])
CPU times: user 289 ms, sys: 9.96 ms, total: 299 ms
Wall time: 293 ms

นอกจากนี้เรายังอาจเปรียบเทียบกับโซลูชันก่อนหน้านี้ (สำหรับค่าที่ต่ำกว่าของขนาดของประชากร)

In [5]: populationSize, sampleSize = 10**8, 10**5

In [6]: %time lst = random.sample(range(populationSize), sampleSize)
CPU times: user 1.89 s, sys: 299 ms, total: 2.19 s
Wall time: 2.18 s

In [7]: %time lst1 = extractSamples(populationSize, sampleSize, [(0, populationSize-1)])
CPU times: user 449 ms, sys: 8.92 ms, total: 458 ms
Wall time: 442 ms

โปรดทราบว่าฉันลดpopulationSizeค่าลงเนื่องจากสร้าง Memory Error สำหรับค่าที่สูงขึ้นเมื่อใช้random.sampleโซลูชัน (กล่าวถึงในคำตอบก่อนหน้านี้ที่นี่และที่นี่ ) สำหรับค่าข้างต้นเราสามารถสังเกตได้ว่ามีextractSamplesประสิทธิภาพดีกว่าrandom.sampleแนวทางดังกล่าว

PS: แม้ว่าแนวทางหลักจะคล้ายกับคำตอบก่อนหน้าของฉันแต่ก็มีการปรับเปลี่ยนอย่างมากในการนำไปใช้รวมทั้งแนวทางพร้อมกับการปรับปรุงความชัดเจน


0

ปัญหาเกี่ยวกับวิธีการตามชุด ("ถ้าค่าสุ่มเป็นค่าที่ส่งคืนให้ลองอีกครั้ง") คือรันไทม์ของพวกเขาไม่ถูกกำหนดเนื่องจากการชนกัน (ซึ่งต้องใช้การทำซ้ำ "ลองอีกครั้ง" อีกครั้ง) โดยเฉพาะอย่างยิ่งเมื่อมีการส่งคืนค่าสุ่มจำนวนมาก จากช่วง

ทางเลือกอื่นที่ไม่มีแนวโน้มที่จะรันไทม์แบบไม่กำหนดปัจจัยดังต่อไปนี้:

import bisect
import random

def fast_sample(low, high, num):
    """ Samples :param num: integer numbers in range of
        [:param low:, :param high:) without replacement
        by maintaining a list of ranges of values that
        are permitted.

        This list of ranges is used to map a random number
        of a contiguous a range (`r_n`) to a permissible
        number `r` (from `ranges`).
    """
    ranges = [high]
    high_ = high - 1
    while len(ranges) - 1 < num:
        # generate a random number from an ever decreasing
        # contiguous range (which we'll map to the true
        # random number).
        # consider an example with low=0, high=10,
        # part way through this loop with:
        #
        # ranges = [0, 2, 3, 7, 9, 10]
        #
        # r_n :-> r
        #   0 :-> 1
        #   1 :-> 4
        #   2 :-> 5
        #   3 :-> 6
        #   4 :-> 8
        r_n = random.randint(low, high_)
        range_index = bisect.bisect_left(ranges, r_n)
        r = r_n + range_index
        for i in xrange(range_index, len(ranges)):
            if ranges[i] <= r:
                # as many "gaps" we iterate over, as much
                # is the true random value (`r`) shifted.
                r = r_n + i + 1
            elif ranges[i] > r_n:
                break
        # mark `r` as another "gap" of the original
        # [low, high) range.
        ranges.insert(i, r)
        # Fewer values possible.
        high_ -= 1
    # `ranges` happens to contain the result.
    return ranges[:-1]

0
import random

sourcelist=[]
resultlist=[]

for x in range(100):
    sourcelist.append(x)

for y in sourcelist:
    resultlist.insert(random.randint(0,len(resultlist)),y)

print (resultlist)

1
ยินดีต้อนรับสู่ Stackoverflow โปรดอธิบายคำตอบของคุณว่าเหตุใดจึงแก้ปัญหาเพื่อให้ผู้อื่นเข้าใจคำตอบของคุณได้ง่าย
ปลาหมึก

แม้ว่ารหัสนี้จะช่วยแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมจึงแก้ปัญหานี้ได้จะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้อย่างแท้จริงและอาจส่งผลให้มีการโหวตเพิ่มขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่แค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้ จากรีวิว
double-beep

-1

หากคุณต้องการให้แน่ใจว่าตัวเลขที่เพิ่มนั้นไม่ซ้ำกันคุณสามารถใช้Set object

ถ้าใช้ 2.7 ขึ้นไปหรือนำเข้าโมดูลชุดถ้าไม่

ดังที่คนอื่น ๆ กล่าวไว้นั่นหมายความว่าตัวเลขไม่ได้เป็นแบบสุ่ม


-1

เป็นจำนวนเต็มตัวอย่างโดยไม่ต้องแทนที่ระหว่างminvalและmaxval:

import numpy as np

minval, maxval, n_samples = -50, 50, 10
generator = np.random.default_rng(seed=0)
samples = generator.permutation(np.arange(minval, maxval))[:n_samples]

# or, if minval is 0,
samples = generator.permutation(maxval)[:n_samples]

กับ jax:

import jax

minval, maxval, n_samples = -50, 50, 10
key = jax.random.PRNGKey(seed=0)
samples = jax.random.shuffle(key, jax.numpy.arange(minval, maxval))[:n_samples]

ทำไมคุณจะสร้าง permutaiton ของจำนวนมากอาจจะเป็นขององค์ประกอบและหลังจากนั้นเพียงเลือกแรกn_samplesของพวกเขา? คุณมีเหตุผลอะไรที่อยู่เบื้องหลังแนวทางนี้ คุณอธิบายได้ไหมว่าอะไรคือข้อดีของแนวทางของคุณเมื่อเทียบกับคำตอบที่มีอยู่จำนวนมาก (ส่วนใหญ่จาก 8 ปีที่แล้ว)
kyrill

จริงๆแล้วคำตอบของฉันมีความซับซ้อนใกล้เคียงกับคำตอบอื่น ๆ ที่ได้รับการโหวตสูงสุดและเร็วกว่าเพราะใช้ตัวเลข วิธีอื่น ๆ ที่ได้รับการโหวตสูงสุดrandom.shuffleซึ่งใช้ Mersenne Twister ซึ่งช้ากว่า algos ที่นำเสนอโดย numpy (และอาจเป็น jax) numpy และ jax อนุญาตให้ใช้อัลกอริทึมการสร้างตัวเลขสุ่มอื่น ๆ jax ยังช่วยให้สามารถรวบรวม jit และสร้างความแตกต่างซึ่งจะเป็นประโยชน์สำหรับการสร้างความแตกต่างแบบสุ่ม นอกจากนี้เกี่ยวกับอาร์เรย์ที่ "อาจมีขนาดใหญ่" คำตอบที่ได้รับการโหวตสูงสุดบางคำตอบก็ทำในสิ่งเดียวกันrandom.shuffleซึ่งฉันไม่คิดว่าเป็นบาปในแง่สัมพัทธ์หรือแม้แต่ความรู้สึกที่สมบูรณ์
grisaitis

1
ไม่แน่ใจว่า " random.shuffleใช้ Mersenne Twister" หมายถึงอะไร- เป็นการสับเปลี่ยนของ Fisher-Yates ดังที่ได้กล่าวไว้ในคำตอบหลายข้อ มีความซับซ้อนของเวลาเชิงเส้นดังนั้นจึงไม่อาจช้ากว่าอัลกอริทึมที่นำเสนอโดยไลบรารีอื่น ๆ ไม่ได้เป็นตัวเลขหรืออย่างอื่น หาก numpy เร็วกว่านั้นเป็นเพียงเพราะมันถูกแทนที่ด้วย C แต่สิ่งนี้ไม่รับประกันว่าจะสร้างการเปลี่ยนแปลงขนาดใหญ่ (ซึ่งอาจไม่พอดีกับหน่วยความจำ) ให้เลือกเพียงไม่กี่องค์ประกอบจากมัน ไม่มีคำตอบเดียวนอกจากของคุณที่ทำเช่นนี้
kyrill

ฉันขอโทษฉันอ่านว่า python random ใช้ Mersenne Twister เพราะเป็น prng คุณมีแหล่งข้อมูลเพื่อให้ฉันสามารถเรียนรู้เพิ่มเติมเกี่ยวกับฟิชเชอร์เยตส์และบทบาทใน random.shuffle หรือไม่?
grisaitis

มีลิงก์ไปยัง Wikipedia สองลิงก์แยกกันสำหรับคำตอบสองคำที่นี่ หาก Wikipedia ไม่ใช่แหล่งข้อมูลที่ดีพอสำหรับคุณมีการอ้างอิง 14 รายการที่ท้ายบทความ แล้วก็มี Google ที่ช่วย? โอ้randomโมดูลเขียนด้วย Python ดังนั้นคุณสามารถดูซอร์สได้อย่างง่ายดาย (ลองrandom.__file__)
kyrill

-3

จาก CLI ใน win xp:

python -c "import random; print(sorted(set([random.randint(6,49) for i in range(7)]))[:6])"

ในแคนาดาเรามี Lotto 6/49 ฉันเพียงแค่ห่อโค้ดข้างต้นใน lotto.bat และเรียกใช้หรือเพียงแค่C:\home\lotto.batC:\home\lotto

เนื่องจากrandom.randintมักจะซ้ำตัวเลขฉันจึงใช้setกับrange(7)แล้วย่อให้ยาวเป็น 6

ในบางครั้งหากตัวเลขซ้ำกันมากกว่า 2 ครั้งความยาวของรายการผลลัพธ์จะน้อยกว่า 6

แก้ไข: อย่างไรก็ตามrandom.sample(range(6,49),6)เป็นวิธีที่ถูกต้องในการไป


-3
import random
result=[]
for i in range(1,50):
    rng=random.randint(1,20)
    result.append(rng)

1
คุณช่วยอธิบายได้ไหมว่าวิธีนี้หลีกเลี่ยงรายการซ้ำได้อย่างไร ไม่ชัดเจนจากการถ่ายโอนรหัสนี้
Toby Speight

มันไม่ print len(result), len(set(result)). คุณจะคาดหวังที่จะเห็นว่าresultจะมีองค์ประกอบที่ไม่ซ้ำกันเพียงครั้งเดียวทุก1.0851831788708547256608362340568947172111832359638926... × 10^20พยายาม
Jedi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.