Các số ngẫu nhiên duy nhất (không lặp lại) trong O (1)?


179

Tôi muốn tạo các số ngẫu nhiên duy nhất trong khoảng từ 0 đến 1000 không bao giờ lặp lại (tức là 6 không hiển thị hai lần), nhưng điều đó không dùng đến một cái gì đó như tìm kiếm O (N) của các giá trị trước đó để làm điều đó. Điều này có thể không?


4
Đây không phải là câu hỏi tương tự như stackoverflow.com/questions/158716/ trên
jk.

2
Là 0 giữa 0 và 1000?
Pete Kirkham

4
Nếu bạn đang cấm bất cứ điều gì trong thời gian liên tục (như O(n)thời gian hoặc bộ nhớ), thì nhiều câu trả lời dưới đây là sai, bao gồm cả câu trả lời được chấp nhận.
jww

Làm thế nào bạn sẽ xáo trộn một gói thẻ?
Đại tá Panic

9
CẢNH BÁO! Nhiều câu trả lời được đưa ra dưới đây để không tạo ra các chuỗi thực sự ngẫu nhiên , chậm hơn O (n) hoặc khiếm khuyết! mã hóa kinh dị.com / blog / archives / 2001015.html là một nội dung cần thiết trước khi bạn sử dụng bất kỳ trong số chúng hoặc cố gắng tự pha chế!
ivan_pozdeev

Câu trả lời:


247

Khởi tạo một mảng gồm 1001 số nguyên với các giá trị 0-1000 và đặt một biến, max, thành chỉ mục tối đa hiện tại của mảng (bắt đầu bằng 1000). Chọn một số ngẫu nhiên, r, từ 0 đến tối đa, hoán đổi số tại vị trí r với số ở vị trí tối đa và trả về số ngay tại vị trí tối đa. Giảm tối đa 1 và tiếp tục. Khi max là 0, đặt max trở lại kích thước của mảng - 1 và bắt đầu lại mà không cần phải xác định lại mảng.

Cập nhật: Mặc dù tôi đã tự mình nghĩ ra phương pháp này khi trả lời câu hỏi, sau một số nghiên cứu tôi nhận ra đây là phiên bản sửa đổi của Fisher-Yates tên là Durstenfeld-Fisher-Yates hoặc Knuth-Fisher-Yates. Vì mô tả có thể hơi khó theo dõi, tôi đã cung cấp một ví dụ bên dưới (sử dụng 11 yếu tố thay vì 1001):

Mảng bắt đầu với 11 phần tử được khởi tạo thành mảng [n] = n, max bắt đầu lúc 10:

+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|
+--+--+--+--+--+--+--+--+--+--+--+
                                ^
                               max    

Ở mỗi lần lặp, một số ngẫu nhiên r được chọn giữa 0 và max, mảng [r] và mảng [max] được hoán đổi, mảng mới [max] được trả về và max bị giảm:

max = 10, r = 3
           +--------------------+
           v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 7| 8| 9| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 9, r = 7
                       +-----+
                       v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 9| 8| 7: 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 8, r = 1
     +--------------------+
     v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 5| 6| 9| 1: 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 7, r = 5
                 +-----+
                 v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 9| 6| 5: 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

...

Sau 11 lần lặp, tất cả các số trong mảng đã được chọn, max == 0 và các phần tử mảng được xáo trộn:

+--+--+--+--+--+--+--+--+--+--+--+
| 4|10| 8| 6| 2| 0| 9| 5| 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

Tại thời điểm này, tối đa có thể được đặt lại thành 10 và quá trình có thể tiếp tục.


6
Bài đăng của Jeff về xáo trộn cho thấy điều này sẽ không trả về những con số ngẫu nhiên tốt .. mã hóa kinh dị.com / blog / archives / 2001015.html
pro

14
@Peter Rounce: Tôi nghĩ là không; điều này đối với tôi giống như thuật toán Fisher Yates, cũng được trích dẫn trong bài đăng của Jeff (với tư cách là người tốt).
Brent.Longborough

3
@robert: Tôi chỉ muốn chỉ ra rằng nó không tạo ra, như trong tên của câu hỏi, "số ngẫu nhiên duy nhất trong O (1)".
Charles

3
@mikera: Đồng ý, mặc dù về mặt kỹ thuật nếu bạn đang sử dụng số nguyên có kích thước cố định, toàn bộ danh sách có thể được tạo trong O (1) (với hằng số lớn, viz. 2 ^ 32). Ngoài ra, với mục đích thực tế, định nghĩa "ngẫu nhiên" rất quan trọng - nếu bạn thực sự muốn sử dụng nhóm entropy của hệ thống, giới hạn là tính toán của các bit ngẫu nhiên thay vì tự tính toán và trong trường hợp đó, log n có liên quan lần nữa. Nhưng trong trường hợp có khả năng bạn sẽ sử dụng (tương đương) / dev / urandom chứ không phải / dev / ngẫu nhiên, bạn sẽ quay lại 'thực tế' O (n).
Charles

4
Tôi hơi bối rối, có phải thực tế là bạn phải thực hiện các Nlần lặp (11 trong ví dụ này) để có được kết quả mong muốn mỗi lần như O(n)vậy không? Vì bạn cần phải thực hiện các Nbước lặp để có được các N!kết hợp từ cùng một trạng thái ban đầu, nếu không, đầu ra của bạn sẽ chỉ là một trong N trạng thái.
Seph

71

Bạn có thể làm được việc này:

  1. Tạo một danh sách, 0..1000.
  2. Xáo trộn danh sách. (Xem shuffle Fisher-Yates để biết cách tốt để làm điều này.)
  3. Trả về số theo thứ tự từ danh sách xáo trộn.

Vì vậy, điều này không yêu cầu tìm kiếm các giá trị cũ mỗi lần, nhưng nó vẫn yêu cầu O (N) cho lần xáo trộn ban đầu. Nhưng như Nils đã chỉ ra trong các bình luận, đây là khoản khấu hao O (1).


5
@Just Một số Guy N = 1000, vì vậy bạn đang nói rằng đó là O (N / N) là O (1)
Guvante

1
Nếu mỗi lần chèn vào mảng được xáo trộn là một thao tác, thì sau khi chèn 1 giá trị, bạn có thể nhận được 1 giá trị ngẫu nhiên. 2 cho 2 giá trị, v.v., n cho n giá trị. Phải mất n thao tác để tạo danh sách, vì vậy toàn bộ thuật toán là O (n). Nếu bạn cần 1.000.000 giá trị ngẫu nhiên, sẽ mất 1.000.000 ops
Kibbee

3
Hãy nghĩ về nó theo cách này, nếu đó là thời gian không đổi, nó sẽ mất cùng một lượng thời gian cho 10 số ngẫu nhiên như với 10 tỷ. Nhưng do việc xáo trộn lấy O (n), chúng tôi biết điều này không đúng.
Kibbee

1
Điều này thực sự mất thời gian khấu hao O (log n), vì bạn cần tạo n bit n ngẫu nhiên.
Charles

2
Và bây giờ, tôi có tất cả các biện minh để làm điều đó! meta.stackoverflow.com/q/252503/13
Chris Jester-Young

60

Sử dụng Đăng ký thay đổi phản hồi tuyến tính tối đa .

Nó có thể thực hiện được trong một vài dòng C và trong thời gian chạy thực hiện ít hơn một vài thử nghiệm / nhánh, một chút bổ sung và dịch chuyển bit. Nó không ngẫu nhiên, nhưng nó đánh lừa hầu hết mọi người.


12
"Nó không ngẫu nhiên, nhưng nó đánh lừa hầu hết mọi người". Điều đó áp dụng cho tất cả các trình tạo số giả ngẫu nhiên và tất cả các câu trả lời khả thi cho câu hỏi này. Nhưng hầu hết mọi người sẽ không nghĩ về nó. Vì vậy, bỏ qua ghi chú này có thể dẫn đến nhiều lượt upvote hơn ...
f3lix

3
@bobobobo: Bộ nhớ O (1) là tại sao.
Tro

3
Nit: đó là bộ nhớ O (log N).
Paul Hankin

2
Sử dụng phương pháp đó, làm thế nào để bạn tạo ra các số, hãy nói từ 0 đến 800000? Một số người có thể sử dụng LFSR trong khoảng thời gian là 1048575 (2 ^ 20 - 1) và nhận được lần tiếp theo nếu số nằm ngoài phạm vi nhưng điều này sẽ không hiệu quả.
tigrou

1
Là một LFSR, điều này không tạo ra các chuỗi phân phối đồng đều : toàn bộ chuỗi sẽ được tạo bởi phần tử đầu tiên.
ivan_pozdeev

21

Bạn có thể sử dụng Trình tạo cộng tuyến tuyến tính . Trong đó m(mô-đun) sẽ là số nguyên tố gần nhất lớn hơn 1000. Khi bạn nhận được một số trong phạm vi, chỉ cần lấy số tiếp theo. Trình tự sẽ chỉ lặp lại sau khi tất cả các yếu tố đã xảy ra và bạn không phải sử dụng bảng. Hãy nhận biết những nhược điểm của máy phát điện này (bao gồm thiếu tính ngẫu nhiên).


1
1009 là số nguyên tố đầu tiên sau 1000.
Teepeemm

LCG có mối tương quan cao giữa các số liên tiếp, do đó, các kết hợp sẽ không hoàn toàn ngẫu nhiên ở mức lớn (ví dụ: các số xa hơn so với knhau trong chuỗi không bao giờ có thể xảy ra cùng nhau).
ivan_pozdeev

m nên là số phần tử 1001 (1000 + 1 cho số không) và bạn có thể sử dụng Next = (1002 * Hiện tại + 757) mod 1001;
Max Abramovich

21

Bạn có thể sử dụng Mã hóa bảo quản định dạng để mã hóa bộ đếm. Bộ đếm của bạn chỉ đi từ 0 trở lên và mã hóa sử dụng khóa bạn chọn để biến nó thành một giá trị dường như ngẫu nhiên của bất kỳ cơ số và chiều rộng nào bạn muốn. Ví dụ cho ví dụ trong câu hỏi này: cơ số 10, chiều rộng 3.

Mật mã khối thường có kích thước khối cố định, ví dụ 64 hoặc 128 bit. Nhưng Mã hóa bảo tồn định dạng cho phép bạn lấy một mật mã tiêu chuẩn như AES và tạo một mật mã có chiều rộng nhỏ hơn, bất kể cơ số và độ rộng nào bạn muốn, với thuật toán vẫn mạnh về mặt mật mã.

Nó được đảm bảo không bao giờ có xung đột (vì các thuật toán mã hóa tạo ra ánh xạ 1: 1). Nó cũng có thể đảo ngược (ánh xạ 2 chiều), vì vậy bạn có thể lấy số kết quả và quay lại giá trị bộ đếm mà bạn đã bắt đầu.

Kỹ thuật này không cần bộ nhớ để lưu trữ một mảng bị xáo trộn, v.v., đây có thể là một lợi thế trên các hệ thống có bộ nhớ hạn chế.

AES-FFX là một phương pháp tiêu chuẩn được đề xuất để đạt được điều này. Tôi đã thử nghiệm với một số mã Python cơ bản dựa trên ý tưởng AES-FFX, mặc dù không hoàn toàn phù hợp-- xem mã Python tại đây . Ví dụ, nó có thể mã hóa bộ đếm thành số thập phân 7 chữ số trông ngẫu nhiên hoặc số 16 bit. Dưới đây là một ví dụ về cơ số 10, chiều rộng 3 (để đưa ra một số trong khoảng từ 0 đến 999) như câu hỏi đã nêu:

000   733
001   374
002   882
003   684
004   593
005   578
006   233
007   811
008   072
009   337
010   119
011   103
012   797
013   257
014   932
015   433
...   ...

Để có được các chuỗi giả ngẫu nhiên không lặp lại khác nhau, hãy thay đổi khóa mã hóa. Mỗi khóa mã hóa tạo ra một chuỗi giả ngẫu nhiên không lặp lại khác nhau.


Đây thực chất là một ánh xạ đơn giản, do đó không khác biệt gì so với LCG và LFSR, với tất cả các kink liên quan (ví dụ: các giá trị nhiều hơn knhau trong chuỗi không bao giờ có thể xảy ra cùng nhau).
ivan_pozdeev

@ivan_pozdeev: Tôi đang gặp khó khăn trong việc hiểu ý nghĩa của bình luận của bạn. Bạn có thể giải thích những gì sai với ánh xạ này, "tất cả các nút liên quan" là gì và là kgì không?
Craig McQueen

Tất cả "mã hóa" có hiệu quả ở đây là thay thế chuỗi 1,2,...,Nbằng một chuỗi các số giống nhau trong một số thứ tự khác, nhưng vẫn không đổi. Các số sau đó được kéo từ dãy này từng cái một. klà số lượng giá trị được chọn (OP không chỉ định một chữ cái cho nó nên tôi phải giới thiệu một giá trị).
ivan_pozdeev

3
@ivan_pozdeev Không phải trường hợp FPE phải thực hiện ánh xạ tĩnh cụ thể hay "sự kết hợp được trả về được xác định đầy đủ bởi số đầu tiên". Do tham số cấu hình lớn hơn nhiều so với kích thước của số đầu tiên (chỉ có một nghìn trạng thái), nên có nhiều chuỗi bắt đầu với cùng một giá trị ban đầu và sau đó tiến hành các giá trị tiếp theo khác nhau. Bất kỳ máy phát điện thực tế nào cũng sẽ không bao gồm toàn bộ không gian hoán vị có thể có; Không đáng để nâng chế độ thất bại đó khi OP không yêu cầu.
sh1

4
+1. Khi được triển khai chính xác, sử dụng mật mã khối an toàn với khóa được chọn ngẫu nhiên, các chuỗi được tạo bằng phương pháp này sẽ không thể phân biệt được tính toán với một lần xáo trộn ngẫu nhiên thực sự. Điều đó có nghĩa là, không có cách nào để phân biệt đầu ra của phương thức này với một ngẫu nhiên ngẫu nhiên thực sự nhanh hơn đáng kể so với việc kiểm tra tất cả các khóa mật mã khối có thể và xem liệu có bất kỳ khóa nào tạo ra cùng một đầu ra không. Đối với một mật mã có không gian phím 128 bit, điều này có lẽ vượt quá khả năng tính toán hiện có sẵn cho nhân loại; với các khóa 256-bit, nó có thể sẽ mãi mãi như vậy.
Ilmari Karonen

7

Đối với các số thấp như 0 ... 1000, việc tạo một danh sách chứa tất cả các số và xáo trộn nó là đơn giản. Nhưng nếu tập hợp các số để rút ra là rất lớn thì có một cách thanh lịch khác: Bạn có thể xây dựng một hoán vị giả danh bằng cách sử dụng khóa và hàm băm mật mã. Xem C ++ - mã giả ví dụ sau đây:

unsigned randperm(string key, unsigned bits, unsigned index) {
  unsigned half1 =  bits    / 2;
  unsigned half2 = (bits+1) / 2;
  unsigned mask1 = (1 << half1) - 1;
  unsigned mask2 = (1 << half2) - 1;
  for (int round=0; round<5; ++round) {
    unsigned temp = (index >> half1);
    temp = (temp << 4) + round;
    index ^= hash( key + "/" + int2str(temp) ) & mask1;
    index = ((index & mask2) << half1) | ((index >> half2) & mask1);
  }
  return index;
}

Ở đây, hashchỉ là một số hàm giả ngẫu nhiên tùy ý ánh xạ một chuỗi ký tự thành một số nguyên không dấu lớn có thể. Hàm randpermnày là một hoán vị của tất cả các số trong phạm vi 0 ... pow (2, bit) -1 giả sử một khóa cố định. Điều này theo sau khi xây dựng bởi vì mỗi bước thay đổi biến indexlà có thể đảo ngược. Điều này được lấy cảm hứng từ một mật mã Feistel .


Giống như stackoverflow.com/a/16097246/648265 , thất bại ngẫu nhiên cho các chuỗi giống nhau.
ivan_pozdeev

1
@ivan_pozdeev: Về lý thuyết, giả sử sức mạnh tính toán vô hạn, vâng. Tuy nhiên, giả sử rằng hash(), như được sử dụng trong đoạn mã trên, là một hàm giả ngẫu nhiên an toàn, việc xây dựng này sẽ chứng minh được (Luby & Rackoff, 1988) mang lại một hoán vị giả ngẫu nhiên , không thể phân biệt được với một sự xáo trộn ngẫu nhiên thực sự bằng cách sử dụng ít nỗ lực hơn đáng kể tìm kiếm toàn bộ không gian khóa, theo cấp số nhân theo chiều dài khóa. Ngay cả đối với các khóa có kích thước hợp lý (giả sử là 128 bit), điều này vượt quá tổng sức mạnh tính toán có sẵn trên Trái đất.
Ilmari Karonen

(BTW, chỉ để làm cho lập luận này chặt chẽ hơn một chút, tôi muốn thay thế hash( key + "/" + int2str(temp) )cấu trúc ad hoc ở trên bằng HMAC , có thể giảm thiểu bảo mật đến chức năng nén băm cơ bản. Ngoài ra, sử dụng HMAC có thể ít có khả năng ai đó cố tình sử dụng cấu trúc này với chức năng băm không mã hóa không an toàn.)
Ilmari Karonen

6

Bạn có thể sử dụng thuật toán Xincrol của tôi được mô tả ở đây:

http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html

Đây là một phương pháp thuật toán thuần túy để tạo ra các số ngẫu nhiên nhưng duy nhất không có mảng, danh sách, hoán vị hoặc tải CPU nặng.

Phiên bản mới nhất cũng cho phép đặt phạm vi số, Ví dụ: nếu tôi muốn các số ngẫu nhiên duy nhất trong phạm vi 0-1073741821.

Tôi thực tế đã sử dụng nó cho

  • Trình phát MP3 phát ngẫu nhiên mọi bài hát, nhưng chỉ một lần cho mỗi album / thư mục
  • Hiệu ứng hòa tan khung hình pixel khôn ngoan (nhanh và mượt)
  • Tạo sương mù "nhiễu" bí mật trên hình ảnh cho chữ ký và điểm đánh dấu (steganography)
  • ID đối tượng dữ liệu để tuần tự hóa số lượng lớn các đối tượng Java thông qua cơ sở dữ liệu
  • Bảo vệ ba bit bộ nhớ đa số
  • Mã hóa địa chỉ + giá trị (mỗi byte không chỉ được mã hóa mà còn được chuyển đến một vị trí được mã hóa mới trong bộ đệm). Điều này thực sự khiến các đồng tiền mã hóa phát điên với tôi :-)
  • Văn bản thuần túy đến đơn giản như tiền điện tử Mã hóa văn bản cho SMS, email, v.v.
  • Máy tính Poker Texas Texas của tôi (THC)
  • Một số trò chơi của tôi để mô phỏng, "xáo trộn", xếp hạng
  • hơn

Nó là mở, miễn phí. Hãy thử một lần...


Phương pháp đó có thể làm việc cho một giá trị thập phân, ví dụ: xáo trộn bộ đếm thập phân 3 chữ số để luôn có kết quả thập phân 3 chữ số?
Craig McQueen

Như một ví dụ về thuật toán Xorshift , đó là một LFSR, với tất cả các kink liên quan (ví dụ: các giá trị nhiều hơn knhau trong chuỗi không bao giờ có thể xảy ra cùng nhau).
ivan_pozdeev

5

Bạn thậm chí không cần một mảng để giải quyết vấn đề này.

Bạn cần một bitmask và một bộ đếm.

Khởi tạo bộ đếm về 0 và tăng nó trong các cuộc gọi liên tiếp. XOR bộ đếm với bitmask (được chọn ngẫu nhiên khi khởi động hoặc cố định) để tạo số psuedorandom. Nếu bạn không thể có số lượng vượt quá 1000, đừng sử dụng bitmask rộng hơn 9 bit. (Nói cách khác, bitmask là một số nguyên không quá 511.)

Đảm bảo rằng khi bộ đếm vượt qua 1000, bạn đặt lại về không. Tại thời điểm này, bạn có thể chọn một bitmask ngẫu nhiên khác - nếu bạn muốn - để tạo ra cùng một bộ số theo thứ tự khác nhau.


2
Điều đó sẽ đánh lừa ít người hơn LFSR.
starblue

"bitmask" trong vòng 512 ... 1023 cũng không sao. Đối với một chút ngẫu nhiên giả mạo xem câu trả lời của tôi. :-)
sellibitze

Về cơ bản tương đương với stackoverflow.com/a/16097246/648265 , cũng thất bại ngẫu nhiên cho các chuỗi.
ivan_pozdeev

4

Tôi nghĩ rằng máy phát đồng quy tuyến tính sẽ là giải pháp đơn giản nhất.

nhập mô tả hình ảnh ở đây

và chỉ có 3 hạn chế đối với các giá trị a , cm

  1. m c tương đối nguyên tố,
  2. a - 1 chia hết cho tất cả các thừa số nguyên tố của m
  3. a-1 chia hết cho 4 nếu m chia hết cho 4

PS phương pháp đã được đề cập nhưng bài đăng có một giả định sai về các giá trị không đổi. Các hằng số dưới đây sẽ hoạt động tốt cho trường hợp của bạn

Trong trường hợp của bạn, bạn có thể sử dụng a = 1002 , c = 757,m = 1001

X = (1002 * X + 757) mod 1001

3

Đây là một số mã tôi đã nhập sử dụng logic của giải pháp đầu tiên. Tôi biết đây là "bất khả tri ngôn ngữ" nhưng chỉ muốn trình bày đây là một ví dụ trong C # trong trường hợp bất cứ ai đang tìm kiếm một giải pháp thực tế nhanh chóng.

// Initialize variables
Random RandomClass = new Random();
int RandArrayNum;
int MaxNumber = 10;
int LastNumInArray;
int PickedNumInArray;
int[] OrderedArray = new int[MaxNumber];      // Ordered Array - set
int[] ShuffledArray = new int[MaxNumber];     // Shuffled Array - not set

// Populate the Ordered Array
for (int i = 0; i < MaxNumber; i++)                  
{
    OrderedArray[i] = i;
    listBox1.Items.Add(OrderedArray[i]);
}

// Execute the Shuffle                
for (int i = MaxNumber - 1; i > 0; i--)
{
    RandArrayNum = RandomClass.Next(i + 1);         // Save random #
    ShuffledArray[i] = OrderedArray[RandArrayNum];  // Populting the array in reverse
    LastNumInArray = OrderedArray[i];               // Save Last Number in Test array
    PickedNumInArray = OrderedArray[RandArrayNum];  // Save Picked Random #
    OrderedArray[i] = PickedNumInArray;             // The number is now moved to the back end
    OrderedArray[RandArrayNum] = LastNumInArray;    // The picked number is moved into position
}

for (int i = 0; i < MaxNumber; i++)                  
{
    listBox2.Items.Add(ShuffledArray[i]);
}

3

Kết quả phương pháp này phù hợp khi giới hạn cao và bạn chỉ muốn tạo một vài số ngẫu nhiên.

#!/usr/bin/perl

($top, $n) = @ARGV; # generate $n integer numbers in [0, $top)

$last = -1;
for $i (0 .. $n-1) {
    $range = $top - $n + $i - $last;
    $r = 1 - rand(1.0)**(1 / ($n - $i));
    $last += int($r * $range + 1);
    print "$last ($r)\n";
}

Lưu ý rằng các số được tạo theo thứ tự tăng dần, nhưng sau đó bạn có thể xáo trộn.


Vì điều này tạo ra các kết hợp thay vì hoán vị, nên thích hợp hơn cho stackoverflow.com/questions/2394246/iêu
ivan_pozdeev

1
Thử nghiệm cho thấy điều này có xu hướng đối với các số thấp hơn: xác suất đo được cho các mẫu 2M (top,n)=(100,10)là : (0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635). Tôi đã thử nghiệm bằng Python, vì vậy những khác biệt nhỏ trong toán học có thể đóng một vai trò ở đây (tôi đã đảm bảo tất cả các phép tính để tính toán rlà dấu phẩy động).
ivan_pozdeev

Có, để phương thức này hoạt động chính xác, giới hạn trên phải lớn hơn nhiều so với số lượng giá trị được trích xuất.
salva

Nó sẽ không hoạt động "chính xác" ngay cả khi "giới hạn trên [là] lớn hơn nhiều so với số lượng giá trị" . Các xác suất sẽ vẫn không đồng đều, chỉ bằng một biên độ thấp hơn.
ivan_pozdeev

2

Bạn có thể sử dụng một trình tạo số giả ngẫu nhiên tốt với 10 bit và vứt bỏ 1001 đến 1023 để lại 0 đến 1000.

Từ đây, chúng tôi nhận được thiết kế cho PRNG 10 bit ..

  • 10 bit, đa thức phản hồi x ^ 10 + x ^ 7 + 1 (giai đoạn 1023)

  • sử dụng Galois LFSR để lấy mã nhanh


@Phob Không điều đó sẽ không xảy ra, vì PRNG 10 bit dựa trên Thanh ghi thay đổi phản hồi tuyến tính thường được tạo từ một cấu trúc giả định tất cả các giá trị (trừ một) một lần, trước khi trở về giá trị đầu tiên. Nói cách khác, nó sẽ chỉ chọn 1001 chính xác một lần trong một chu kỳ.
Nuoji

1
@Phob toàn bộ điểm của câu hỏi này là chọn mỗi số chính xác một lần. Và sau đó bạn phàn nàn rằng 1001 sẽ không xảy ra hai lần liên tiếp? Một LFSR với mức chênh lệch tối ưu sẽ đi qua tất cả các số trong không gian của nó theo kiểu ngẫu nhiên giả, sau đó khởi động lại chu kỳ. Nói cách khác, nó không được sử dụng như một hàm ngẫu nhiên thông thường. Khi được sử dụng ngẫu nhiên, chúng ta thường chỉ sử dụng một tập hợp con của các bit. Đọc một chút về nó và nó sẽ sớm có ý nghĩa.
Nuoji

1
Vấn đề duy nhất là một LFSR nhất định chỉ có một chuỗi, do đó tạo ra mối tương quan mạnh mẽ giữa các số được chọn - đặc biệt, không tạo ra mọi kết hợp có thể.
ivan_pozdeev

2
public static int[] randN(int n, int min, int max)
{
    if (max <= min)
        throw new ArgumentException("Max need to be greater than Min");
    if (max - min < n)
        throw new ArgumentException("Range needs to be longer than N");

    var r = new Random();

    HashSet<int> set = new HashSet<int>();

    while (set.Count < n)
    {
        var i = r.Next(max - min) + min;
        if (!set.Contains(i))
            set.Add(i);
    }

    return set.ToArray();
}

N Số lặp ngẫu nhiên không lặp lại sẽ có độ phức tạp O (n), theo yêu cầu.
Lưu ý: Ngẫu nhiên phải tĩnh với áp dụng an toàn luồng.


O (n ^ 2), vì số lần thử lại tỷ lệ trung bình với số lượng phần tử được chọn cho đến nay.
ivan_pozdeev

Hãy suy nghĩ về điều này, nếu bạn chọn min = 0 max = 10000000 và N = 5, hãy thử lại ~ = 0 cho dù có chọn bao nhiêu. Nhưng vâng, bạn có một điểm là nếu max-min nhỏ, o (N) sẽ chia tay.
Erez Robinson

Nếu N << (tối đa) thì nó vẫn tỷ lệ thuận, đó chỉ là hệ số rất nhỏ. Và các hệ số không quan trọng đối với ước tính tiệm cận.
ivan_pozdeev

Đây không phải là O (n). Mỗi lần tập hợp chứa giá trị này và vòng lặp phụ.
paparazzo

2

Giả sử bạn muốn lặp đi lặp lại các danh sách được xáo trộn, không có sự O(n)chậm trễ mỗi lần bạn bắt đầu xáo trộn lại, trong trường hợp đó chúng ta có thể làm điều này:

  1. Tạo 2 danh sách A và B, với 0 đến 1000, chiếm dung lượng 2n.

  2. Danh sách xáo trộn A sử dụng Fisher-Yates, mất nthời gian.

  3. Khi vẽ một số, hãy thực hiện xáo trộn Fisher-Yates 1 bước trong danh sách khác.

  4. Khi con trỏ ở cuối danh sách, chuyển sang danh sách khác.

Tiền xử lý

cursor = 0

selector = A
other    = B

shuffle(A)

Vẽ tranh

temp = selector[cursor]

swap(other[cursor], other[random])

if cursor == N
then swap(selector, other); cursor = 0
else cursor = cursor + 1

return temp

Không cần thiết phải giữ 2 danh sách - hoặc xả hết danh sách trước khi nhìn chằm chằm. Fisher-Yates cho kết quả ngẫu nhiên đồng đều từ bất kỳ trạng thái ban đầu nào. Xem stackoverflow.com/a/158742/648265 để được giải thích.
ivan_pozdeev

@ivan_pozdeev Vâng, đó là kết quả tương tự, nhưng ý tưởng của tôi ở đây là làm cho nó được khấu hao O (1) bằng cách thực hiện phần xáo trộn của hành động vẽ.
Khaled.K

Bạn đã không hiểu. Bạn không cần phải thiết lập lại danh sách trước khi xáo trộn lại. Xáo trộn [1,3,4,5,2]sẽ tạo ra kết quả tương tự như xáo trộn [1,2,3,4,5].
ivan_pozdeev

2

Câu hỏi Làm thế nào để bạn tạo hiệu quả một danh sách K số nguyên không lặp lại giữa 0 và N giới hạn trên được liên kết dưới dạng trùng lặp - và nếu bạn muốn một cái gì đó là O (1) trên mỗi số ngẫu nhiên được tạo (không có O (n) chi phí khởi động)) có một điều chỉnh đơn giản của câu trả lời được chấp nhận.

Tạo một bản đồ không có thứ tự trống (một bản đồ được sắp xếp trống sẽ lấy O (log k) cho mỗi phần tử) từ số nguyên sang số nguyên - thay vì sử dụng một mảng khởi tạo. Đặt tối đa thành 1000 nếu đó là tối đa,

  1. Chọn một số ngẫu nhiên, r, từ 0 đến tối đa.
  2. Đảm bảo rằng cả hai yếu tố bản đồ r và max tồn tại trong bản đồ không có thứ tự. Nếu chúng không tồn tại, hãy tạo chúng với giá trị bằng với chỉ mục của chúng.
  3. Hoán đổi các yếu tố r và max
  4. Trả về phần tử max và giảm tối đa 1 (nếu max bị âm bạn đã hoàn thành).
  5. Quay lại bước 1.

Sự khác biệt duy nhất so với việc sử dụng một mảng khởi tạo là việc khởi tạo các phần tử bị hoãn / bỏ qua - nhưng nó sẽ tạo ra các số chính xác từ cùng một PRNG.


1

Một khả năng khác:

Bạn có thể sử dụng một loạt các cờ. Và lấy cái tiếp theo khi nó đã được chọn.

Nhưng, hãy cẩn thận sau 1000 cuộc gọi, chức năng sẽ không bao giờ kết thúc vì vậy bạn phải thực hiện một biện pháp bảo vệ.


Đây là O (k ^ 2), với một số bước bổ sung tỷ lệ trung bình với số lượng giá trị được chọn cho đến nay.
ivan_pozdeev

1

Dưới đây là một số mã COBOL mẫu mà bạn có thể chơi xung quanh.
Tôi có thể gửi cho bạn tệp RANDGEN.exe để bạn có thể chơi với nó để xem nếu nó muốn bạn muốn.

   IDENTIFICATION DIVISION.
   PROGRAM-ID.  RANDGEN as "ConsoleApplication2.RANDGEN".
   AUTHOR.  Myron D Denson.
   DATE-COMPILED.
  * ************************************************************** 
  *  SUBROUTINE TO GENERATE RANDOM NUMBERS THAT ARE GREATER THAN
  *    ZERO AND LESS OR EQUAL TO THE RANDOM NUMBERS NEEDED WITH NO
  *    DUPLICATIONS.  (CALL "RANDGEN" USING RANDGEN-AREA.)
  *     
  *  CALLING PROGRAM MUST HAVE A COMPARABLE LINKAGE SECTION
  *    AND SET 3 VARIABLES PRIOR TO THE FIRST CALL IN RANDGEN-AREA     
  *
  *    FORMULA CYCLES THROUGH EVERY NUMBER OF 2X2 ONLY ONCE. 
  *    RANDOM-NUMBERS FROM 1 TO RANDOM-NUMBERS-NEEDED ARE CREATED 
  *    AND PASSED BACK TO YOU.
  *
  *  RULES TO USE RANDGEN:
  *
  *    RANDOM-NUMBERS-NEEDED > ZERO 
  *     
  *    COUNT-OF-ACCESSES MUST = ZERO FIRST TIME CALLED.
  *         
  *    RANDOM-NUMBER = ZERO, WILL BUILD A SEED FOR YOU
  *    WHEN COUNT-OF-ACCESSES IS ALSO = 0 
  *     
  *    RANDOM-NUMBER NOT = ZERO, WILL BE NEXT SEED FOR RANDGEN
  *    (RANDOM-NUMBER MUST BE <= RANDOM-NUMBERS-NEEDED)       
  *     
  *    YOU CAN PASS RANDGEN YOUR OWN RANDOM-NUMBER SEED
  *     THE FIRST TIME YOU USE RANDGEN.
  *     
  *    BY PLACING A NUMBER IN RANDOM-NUMBER FIELD
  *      THAT FOLLOWES THESE SIMPLE RULES:
  *        IF COUNT-OF-ACCESSES = ZERO AND 
  *        RANDOM-NUMBER > ZERO AND 
  *        RANDOM-NUMBER <= RANDOM-NUMBERS-NEEDED
  *       
  *    YOU CAN LET RANDGEN BUILD A SEED FOR YOU
  *     
  *      THAT FOLLOWES THESE SIMPLE RULES:
  *        IF COUNT-OF-ACCESSES = ZERO AND 
  *        RANDOM-NUMBER = ZERO AND 
  *        RANDOM-NUMBER-NEEDED > ZERO  
  *         
  *     TO INSURING A DIFFERENT PATTERN OF RANDOM NUMBERS
  *        A LOW-RANGE AND HIGH-RANGE IS USED TO BUILD
  *        RANDOM NUMBERS.
  *        COMPUTE LOW-RANGE =
  *             ((SECONDS * HOURS * MINUTES * MS) / 3).         
  *        A HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE
  *        AFTER RANDOM-NUMBER-BUILT IS CREATED 
  *        AND IS BETWEEN LOW AND HIGH RANGE
  *        RANDUM-NUMBER = RANDOM-NUMBER-BUILT - LOW-RANGE
  *               
  * **************************************************************         
   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
   DATA DIVISION.
   FILE SECTION.
   WORKING-STORAGE SECTION.
   01  WORK-AREA.
       05  X2-POWER                     PIC 9      VALUE 2. 
       05  2X2                          PIC 9(12)  VALUE 2 COMP-3.
       05  RANDOM-NUMBER-BUILT          PIC 9(12)  COMP.
       05  FIRST-PART                   PIC 9(12)  COMP.
       05  WORKING-NUMBER               PIC 9(12)  COMP.
       05  LOW-RANGE                    PIC 9(12)  VALUE ZERO.
       05  HIGH-RANGE                   PIC 9(12)  VALUE ZERO.
       05  YOU-PROVIDE-SEED             PIC X      VALUE SPACE.
       05  RUN-AGAIN                    PIC X      VALUE SPACE.
       05  PAUSE-FOR-A-SECOND           PIC X      VALUE SPACE.   
   01  SEED-TIME.
       05  HOURS                        PIC 99.
       05  MINUTES                      PIC 99.
       05  SECONDS                      PIC 99.
       05  MS                           PIC 99. 
  *
  * LINKAGE SECTION.
  *  Not used during testing  
   01  RANDGEN-AREA.
       05  COUNT-OF-ACCESSES            PIC 9(12) VALUE ZERO.
       05  RANDOM-NUMBERS-NEEDED        PIC 9(12) VALUE ZERO.
       05  RANDOM-NUMBER                PIC 9(12) VALUE ZERO.
       05  RANDOM-MSG                   PIC X(60) VALUE SPACE.
  *    
  * PROCEDURE DIVISION USING RANDGEN-AREA.
  * Not used during testing 
  *  
   PROCEDURE DIVISION.
   100-RANDGEN-EDIT-HOUSEKEEPING.
       MOVE SPACE TO RANDOM-MSG. 
       IF RANDOM-NUMBERS-NEEDED = ZERO
         DISPLAY 'RANDOM-NUMBERS-NEEDED ' NO ADVANCING
         ACCEPT RANDOM-NUMBERS-NEEDED.
       IF RANDOM-NUMBERS-NEEDED NOT NUMERIC 
         MOVE 'RANDOM-NUMBERS-NEEDED NOT NUMERIC' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF RANDOM-NUMBERS-NEEDED = ZERO
         MOVE 'RANDOM-NUMBERS-NEEDED = ZERO' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF COUNT-OF-ACCESSES NOT NUMERIC
         MOVE 'COUNT-OF-ACCESSES NOT NUMERIC' TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF COUNT-OF-ACCESSES GREATER THAN RANDOM-NUMBERS-NEEDED
         MOVE 'COUNT-OF-ACCESSES > THAT RANDOM-NUMBERS-NEEDED'
           TO RANDOM-MSG
           GO TO 900-EXIT-RANDGEN.
       IF YOU-PROVIDE-SEED = SPACE AND RANDOM-NUMBER = ZERO
         DISPLAY 'DO YOU WANT TO PROVIDE SEED  Y OR N: '
           NO ADVANCING
           ACCEPT YOU-PROVIDE-SEED.  
       IF RANDOM-NUMBER = ZERO AND
          (YOU-PROVIDE-SEED = 'Y' OR 'y')
         DISPLAY 'ENTER SEED ' NO ADVANCING
         ACCEPT RANDOM-NUMBER. 
       IF RANDOM-NUMBER NOT NUMERIC
         MOVE 'RANDOM-NUMBER NOT NUMERIC' TO RANDOM-MSG
         GO TO 900-EXIT-RANDGEN.
   200-RANDGEN-DATA-HOUSEKEEPING.      
       MOVE FUNCTION CURRENT-DATE (9:8) TO SEED-TIME.
       IF COUNT-OF-ACCESSES = ZERO
         COMPUTE LOW-RANGE =
                ((SECONDS * HOURS * MINUTES * MS) / 3).
       COMPUTE RANDOM-NUMBER-BUILT = RANDOM-NUMBER + LOW-RANGE.  
       COMPUTE HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE.
       MOVE X2-POWER TO 2X2.             
   300-SET-2X2-DIVISOR.
       IF 2X2 < (HIGH-RANGE + 1) 
          COMPUTE 2X2 = 2X2 * X2-POWER
           GO TO 300-SET-2X2-DIVISOR.    
  * *********************************************************         
  *  IF FIRST TIME THROUGH AND YOU WANT TO BUILD A SEED.    *
  * ********************************************************* 
       IF COUNT-OF-ACCESSES = ZERO AND RANDOM-NUMBER = ZERO
          COMPUTE RANDOM-NUMBER-BUILT =
                ((SECONDS * HOURS * MINUTES * MS) + HIGH-RANGE).
       IF COUNT-OF-ACCESSES = ZERO        
         DISPLAY 'SEED TIME ' SEED-TIME 
               ' RANDOM-NUMBER-BUILT ' RANDOM-NUMBER-BUILT 
               ' LOW-RANGE  ' LOW-RANGE.          
  * *********************************************     
  *    END OF BUILDING A SEED IF YOU WANTED TO  * 
  * *********************************************               
  * ***************************************************
  * THIS PROCESS IS WHERE THE RANDOM-NUMBER IS BUILT  *  
  * ***************************************************   
   400-RANDGEN-FORMULA.
       COMPUTE FIRST-PART = (5 * RANDOM-NUMBER-BUILT) + 7.
       DIVIDE FIRST-PART BY 2X2 GIVING WORKING-NUMBER 
         REMAINDER RANDOM-NUMBER-BUILT. 
       IF RANDOM-NUMBER-BUILT > LOW-RANGE AND
          RANDOM-NUMBER-BUILT < (HIGH-RANGE + 1)
         GO TO 600-RANDGEN-CLEANUP.
       GO TO 400-RANDGEN-FORMULA.
  * *********************************************     
  *    GOOD RANDOM NUMBER HAS BEEN BUILT        *               
  * *********************************************
   600-RANDGEN-CLEANUP.
       ADD 1 TO COUNT-OF-ACCESSES.
       COMPUTE RANDOM-NUMBER = 
            RANDOM-NUMBER-BUILT - LOW-RANGE. 
  * *******************************************************
  * THE NEXT 3 LINE OF CODE ARE FOR TESTING  ON CONSOLE   *  
  * *******************************************************
       DISPLAY RANDOM-NUMBER.
       IF COUNT-OF-ACCESSES < RANDOM-NUMBERS-NEEDED
        GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.     
   900-EXIT-RANDGEN.
       IF RANDOM-MSG NOT = SPACE
        DISPLAY 'RANDOM-MSG: ' RANDOM-MSG.
        MOVE ZERO TO COUNT-OF-ACCESSES RANDOM-NUMBERS-NEEDED RANDOM-NUMBER. 
        MOVE SPACE TO YOU-PROVIDE-SEED RUN-AGAIN.
       DISPLAY 'RUN AGAIN Y OR N '
         NO ADVANCING.
       ACCEPT RUN-AGAIN.
       IF (RUN-AGAIN = 'Y' OR 'y')
         GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.
       ACCEPT PAUSE-FOR-A-SECOND.
       GOBACK.

1
Tôi không biết điều này có thực sự đáp ứng được nhu cầu của OP hay không, nhưng đạo cụ cho sự đóng góp của COBOL!
Mac

1

Hầu hết các câu trả lời ở đây không đảm bảo rằng họ sẽ không trả lại cùng một số hai lần. Đây là một giải pháp chính xác:

int nrrand(void) {
  static int s = 1;
  static int start = -1;
  do {
    s = (s * 1103515245 + 12345) & 1023;
  } while (s >= 1001);
  if (start < 0) start = s;
  else if (s == start) abort();

  return s;
}

Tôi không chắc chắn rằng các ràng buộc được chỉ định rõ. Người ta giả định rằng sau 1000 đầu ra khác, một giá trị được phép lặp lại, nhưng điều đó ngây thơ cho phép 0 đi theo sau 0 miễn là cả hai xuất hiện ở cuối và bắt đầu của 1000. Ngược lại, trong khi có thể giữ khoảng cách 1000 giá trị khác giữa các lần lặp lại, làm như vậy sẽ buộc một tình huống trong đó trình tự tự lặp lại theo cùng một cách chính xác mỗi lần vì không có giá trị nào khác xảy ra ngoài giới hạn đó.

Đây là một phương pháp luôn đảm bảo ít nhất 500 giá trị khác trước khi có thể lặp lại một giá trị:

int nrrand(void) {
  static int h[1001];
  static int n = -1;

  if (n < 0) {
    int s = 1;
    for (int i = 0; i < 1001; i++) {
      do {
        s = (s * 1103515245 + 12345) & 1023;
      } while (s >= 1001);
      /* If we used `i` rather than `s` then our early results would be poorly distributed. */
      h[i] = s;
    }
    n = 0;
  }

  int i = rand(500);
  if (i != 0) {
      i = (n + i) % 1001;
      int t = h[i];
      h[i] = h[n];
      h[n] = t;
  }
  i = h[n];
  n = (n + 1) % 1001;

  return i;
}

Đây là một LCG, giống như stackoverflow.com/a/196164/648265 , không ngẫu nhiên cho các chuỗi cũng như các kink liên quan khác giống nhau.
ivan_pozdeev

@ivan_pozdeev của tôi tốt hơn LCG vì nó đảm bảo rằng nó sẽ không trả lại một bản sao trong cuộc gọi thứ 1001.
sh1

1

Khi N lớn hơn 1000 và bạn cần vẽ K mẫu ngẫu nhiên, bạn có thể sử dụng một bộ có chứa các mẫu cho đến nay. Đối với mỗi lần rút, bạn sử dụng lấy mẫu từ chối , đây sẽ là thao tác "gần như" O (1), do đó tổng thời gian chạy là gần O (K) với lưu trữ O (N).

Thuật toán này chạy vào va chạm khi K "gần" N. Điều này có nghĩa là thời gian chạy sẽ tệ hơn rất nhiều so với O (K). Một cách khắc phục đơn giản là đảo ngược logic để trong K> N / 2, bạn giữ một bản ghi của tất cả các mẫu chưa được rút ra. Mỗi lần rút sẽ loại bỏ một mẫu khỏi bộ từ chối.

Một vấn đề rõ ràng khác với lấy mẫu từ chối là đó là lưu trữ O (N), đây là tin xấu nếu N nằm trong hàng tỷ hoặc nhiều hơn. Tuy nhiên, có một thuật toán giải quyết vấn đề đó. Thuật toán này được gọi là thuật toán của Vitter sau khi nó là nhà phát minh. Thuật toán được mô tả ở đây . Điểm chính của thuật toán Vitter là sau mỗi lần rút, bạn tính toán bỏ qua ngẫu nhiên bằng cách sử dụng một phân phối nhất định đảm bảo lấy mẫu thống nhất.


Các bạn, làm ơn! Phương pháp Fisher-Yates bị hỏng. Bạn chọn cái đầu tiên có xác suất 1 / N và cái thứ hai có xác suất 1 / (N-1)! = 1 / N. Đây là một phương pháp lấy mẫu thiên vị! Bạn thực sự cần thuật toán của Vittter để giải quyết sai lệch.
Emanuel Landeholm

0

Ngư dân

for i from n−1 downto 1 do
     j ← random integer such that 0 ≤ j ≤ i
     exchange a[j] and a[i]

Nó thực sự là O (n-1) vì bạn chỉ cần một lần hoán đổi cho hai lần cuối
Đây là C #

public static List<int> FisherYates(int n)
{
    List<int> list = new List<int>(Enumerable.Range(0, n));
    Random rand = new Random();
    int swap;
    int temp;
    for (int i = n - 1; i > 0; i--)
    {
        swap = rand.Next(i + 1);  //.net rand is not inclusive
        if(swap != i)  // it can stay in place - if you force a move it is not a uniform shuffle
        {
            temp = list[i];
            list[i] = list[swap];
            list[swap] = temp;
        }
    }
    return list;
}

Đã có câu trả lời với điều này nhưng nó khá dài và không nhận ra bạn có thể dừng lại ở 1 (không phải 0)
paparazzo

0

Xin vui lòng xem câu trả lời của tôi tại https://stackoverflow.com/a/46807110/8794687

Đây là một trong những thuật toán đơn giản nhất có độ phức tạp thời gian trung bình O ( s log s ), s biểu thị kích thước mẫu. Ngoài ra còn có một số liên kết ở đó với các thuật toán bảng băm mà độ phức tạp được tuyên bố là O ( s ).


-1

Ai đó đã đăng "tạo số ngẫu nhiên trong excel". Tôi đang sử dụng lý tưởng này. Tạo cấu trúc với 2 phần, str.index và str.ran; Cho 10 số ngẫu nhiên tạo ra một mảng gồm 10 cấu trúc. Đặt str.index từ 0 đến 9 và str.ran thành số ngẫu nhiên khác nhau.

for(i=0;i<10; ++i) {
      arr[i].index = i;
      arr[i].ran   = rand();
}

Sắp xếp mảng trên các giá trị trong mảng [i] .ran. Str.index hiện theo thứ tự ngẫu nhiên. Dưới đây là mã c:

#include <stdio.h>
#include <stdlib.h>

struct RanStr { int index; int ran;};
struct RanStr arr[10];

int sort_function(const void *a, const void *b);

int main(int argc, char *argv[])
{
   int cnt, i;

   //seed(125);

   for(i=0;i<10; ++i)
   {
      arr[i].ran   = rand();
      arr[i].index = i;
      printf("arr[%d] Initial Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
   }

   qsort( (void *)arr, 10, sizeof(arr[0]), sort_function);
   printf("\n===================\n");
   for(i=0;i<10; ++i)
   {
      printf("arr[%d] Random  Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
   }

   return 0;
}

int sort_function(const void *a, const void *b)
{
   struct RanStr *a1, *b1;

   a1=(struct RanStr *) a;
   b1=(struct RanStr *) b;

   return( a1->ran - b1->ran );
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.