Lấy mẫu ngẫu nhiên mà không thay thế


10

Tạo một hàm sẽ xuất ra một tập hợp các số ngẫu nhiên riêng biệt được rút ra từ một phạm vi. Thứ tự của các phần tử trong tập hợp là không quan trọng (chúng thậm chí có thể được sắp xếp), nhưng nội dung của tập hợp phải khác nhau mỗi khi hàm được gọi.

Hàm sẽ nhận được 3 tham số theo bất kỳ thứ tự nào bạn muốn:

  1. Đếm số trong bộ đầu ra
  2. Giới hạn dưới (bao gồm)
  3. Giới hạn trên (bao gồm)

Giả sử tất cả các số là số nguyên trong phạm vi 0 (đã bao gồm) đến 2 31 (độc quyền). Đầu ra có thể được chuyển trở lại bất kỳ cách nào bạn muốn (ghi vào bàn điều khiển, dưới dạng một mảng, v.v.)

Đánh giá

Tiêu chí bao gồm 3 R

  1. Thời gian chạy - được thử nghiệm trên máy Windows 7 lõi ​​tứ với bất kỳ trình biên dịch nào có sẵn miễn phí hoặc dễ dàng (cung cấp liên kết nếu cần)
  2. Tính mạnh mẽ - chức năng xử lý các trường hợp góc hay nó sẽ rơi vào một vòng lặp vô hạn hoặc tạo ra kết quả không hợp lệ - một ngoại lệ hoặc lỗi trên đầu vào không hợp lệ là hợp lệ
  3. Tính ngẫu nhiên - nó sẽ tạo ra kết quả ngẫu nhiên không dễ dự đoán với phân phối ngẫu nhiên. Sử dụng trình tạo số ngẫu nhiên tích hợp là tốt. Nhưng không nên có sự thiên vị rõ ràng hoặc mô hình dự đoán rõ ràng. Cần phải tốt hơn trình tạo số ngẫu nhiên được sử dụng bởi Phòng Kế toán ở Dilbert

Nếu nó mạnh mẽ và ngẫu nhiên thì nó sẽ chuyển sang thời gian chạy. Không mạnh mẽ hoặc ngẫu nhiên làm tổn thương rất nhiều thứ hạng của nó.


Là đầu ra phải vượt qua một cái gì đó như DIEHARD hoặc TestU01 bài kiểm tra , hoặc bạn sẽ đánh giá tính ngẫu nhiên của nó như thế nào? Ồ, và mã nên chạy ở chế độ 32 hay 64 bit? (Điều đó sẽ tạo ra sự khác biệt lớn để tối ưu hóa.)
Ilmari Karonen

TestU01 có lẽ là một chút khắc nghiệt, tôi đoán. Có tiêu chí 3 ngụ ý phân phối đồng đều? Ngoài ra, tại sao yêu cầu không lặp lại ? Điều đó không đặc biệt ngẫu nhiên, sau đó.
Joey

@Joey, chắc chắn rồi. Đó là lấy mẫu ngẫu nhiên mà không cần thay thế. Miễn là không ai tuyên bố rằng các vị trí khác nhau trong danh sách là các biến ngẫu nhiên độc lập thì không có vấn đề gì.
Peter Taylor

À, thực sự. Nhưng tôi không chắc liệu có thư viện và công cụ được thiết lập tốt để đo tính ngẫu nhiên của việc lấy mẫu hay không :-)
Joey

@IlmariKaronen: RE: Tính ngẫu nhiên: Tôi đã thấy các triển khai trước đó rất đáng tiếc. Hoặc là họ có thành kiến ​​nặng nề, hoặc thiếu khả năng tạo ra kết quả khác nhau trong các lần chạy liên tiếp. Vì vậy, chúng tôi không nói về tính ngẫu nhiên ở cấp độ mật mã, mà là ngẫu nhiên hơn trình tạo số ngẫu nhiên của Phòng Kế toán ở Dilbert .
Jim McKeeth

Câu trả lời:


6

Con trăn

import random

def sample(n, lower, upper):
    result = []
    pool = {}
    for _ in xrange(n):
        i = random.randint(lower, upper)
        x = pool.get(i, i)
        pool[i] = pool.get(lower, lower)
        lower += 1
        result.append(x)
    return result

Tôi có lẽ chỉ cần phát minh lại một số thuật toán nổi tiếng, nhưng ý tưởng là (về mặt khái niệm) thực hiện việc xáo trộn một phần Fisher-Yates của phạm vi lower..upperđể có được ntiền tố độ dài của một phạm vi được xáo trộn đồng đều.

Tất nhiên, việc lưu trữ toàn bộ phạm vi sẽ khá tốn kém, vì vậy tôi chỉ lưu trữ các vị trí nơi các yếu tố đã được hoán đổi.

Theo cách này, thuật toán sẽ hoạt động tốt cả trong trường hợp bạn lấy mẫu số từ phạm vi hẹp (ví dụ 1000 số trong phạm vi 1..1000), cũng như trong trường hợp bạn lấy mẫu số từ phạm vi lớn .

Tôi không chắc chắn về chất lượng ngẫu nhiên từ trình tạo tích hợp trong Python, nhưng việc trao đổi trong bất kỳ trình tạo nào có thể tạo ra các số nguyên thống nhất từ ​​một phạm vi tương đối đơn giản.


1
Python sử dụng Mersenne Twister , vì vậy nó tương đối tốt.
ESultanik

1

trăn 2.7

import random
print(lambda x,y,z:random.sample(xrange(y,z),x))(input(),input(),input())

không chắc chắn vị trí của bạn là gì khi sử dụng các phương thức ngẫu nhiên dựng sẵn, nhưng ở đây bạn vẫn đi. đẹp và ngắn

chỉnh sửa: chỉ cần lưu ý rằng phạm vi () không muốn tạo danh sách lớn. dẫn đến lỗi bộ nhớ. sẽ xem liệu có cách nào khác để làm điều này không ...

edit2: phạm vi là hàm sai, xrange hoạt động. Số nguyên tối đa thực sự là2**31-1 dành cho python

kiểm tra:

python sample.py
10
0
2**31-1
[786475923, 2087214992, 951609341, 1894308203, 173531663, 211170399, 426989602, 1909298419, 1424337410, 2090382873]

1

C

Trả về một mảng chứa x int ngẫu nhiên duy nhất giữa min và max. (người gọi phải miễn phí)

#include <stdlib.h>
#include <stdint.h>
#define MAX_ALLOC ((uint32_t)0x40000000)  //max allocated bytes, fix per platform
#define MAX_SAMPLES (MAX_ALLOC/sizeof(uint32_t))

int* randsamp(uint32_t x, uint32_t min, uint32_t max)
{
   uint32_t r,i=x,*a;
   if (!x||x>MAX_SAMPLES||x>(max-min+1)) return NULL;
   a=malloc(x*sizeof(uint32_t));
   while (i--) {
      r= (max-min+1-i);
      a[i]=min+=(r ? rand()%r : 0);
      min++;
   }
   while (x>1) {
      r=a[i=rand()%x--];
      a[i]=a[x];
      a[x]=r;
   }
   return a;
}

Hoạt động bằng cách tạo x số nguyên ngẫu nhiên liên tiếp trong phạm vi, sau đó xáo trộn chúng. Thêm một seed(time)nơi nào đó trong người gọi nếu bạn không muốn có kết quả tương tự mỗi lần chạy.


1

Hồng ngọc> = 1,8,7

def pick(num, min, max)
  (min..max).to_a.sample(num)
end

p pick(5, 10, 20) #=>[12, 18, 13, 11, 10]

1

R

s <- function(n, lower, upper) sample(lower:upper,n); s(10,0,2^31-2)

1

Câu hỏi không chính xác. Bạn có cần lấy mẫu thống nhất hay không? Trong trường hợp cần lấy mẫu thống nhất, tôi có mã sau trong R, có độ phức tạp trung bình O ( s log s ), trong đó s là cỡ mẫu.

# The Tree growing algorithm for uniform sampling without replacement
# by Pavel Ruzankin 
quicksample = function (n,size)
# n - the number of items to choose from
# size - the sample size
{
  s=as.integer(size)
  if (s>n) {
    stop("Sample size is greater than the number of items to choose from")
  }
  # upv=integer(s) #level up edge is pointing to
  leftv=integer(s) #left edge is poiting to; must be filled with zeros
  rightv=integer(s) #right edge is pointig to; must be filled with zeros
  samp=integer(s) #the sample
  ordn=integer(s) #relative ordinal number

  ordn[1L]=1L #initial value for the root vertex
  samp[1L]=sample(n,1L) 
  if (s > 1L) for (j in 2L:s) {
    curn=sample(n-j+1L,1L) #current number sampled
    curordn=0L #currend ordinal number
    v=1L #current vertice
    from=1L #how have come here: 0 - by left edge, 1 - by right edge
    repeat {
      curordn=curordn+ordn[v]
      if (curn+curordn>samp[v]) { #going down by the right edge
        if (from == 0L) {
          ordn[v]=ordn[v]-1L
        }
        if (rightv[v]!=0L) {
          v=rightv[v]
          from=1L
        } else { #creating a new vertex
          samp[j]=curn+curordn
          ordn[j]=1L
          # upv[j]=v
          rightv[v]=j
          break
        }
      } else { #going down by the left edge
        if (from==1L) {
          ordn[v]=ordn[v]+1L
        }
        if (leftv[v]!=0L) {
          v=leftv[v]
          from=0L
        } else { #creating a new vertex
          samp[j]=curn+curordn-1L
          ordn[j]=-1L
          # upv[j]=v
          leftv[v]=j
          break
        }
      }
    }
  }
  return(samp)  
}

Tất nhiên, người ta có thể viết lại bằng C để có hiệu suất tốt hơn. Sự phức tạp của thuật toán này được thảo luận trong: Rouzankin, PS; Voytishek, AV Về chi phí của các thuật toán để lựa chọn ngẫu nhiên. Phương pháp Monte Carlo Appl. 5 (1999), không. 1, 39-54. http://dx.doi.org/10.1515/mcma.1999.5.1.39

Bạn có thể xem qua bài báo này để biết một thuật toán khác có cùng độ phức tạp trung bình.

Nhưng nếu bạn không cần lấy mẫu thống nhất, chỉ yêu cầu tất cả các số được lấy mẫu là khác nhau, thì tình huống sẽ thay đổi đáng kể. Không khó để viết một thuật toán có độ phức tạp trung bình O ( s ).

Xem thêm để lấy mẫu thống nhất: P. Gupta, GP Bhattacharjee. (1984) Một thuật toán hiệu quả để lấy mẫu ngẫu nhiên mà không cần thay thế. Tạp chí quốc tế về Toán máy tính 16: 4, trang 201-209. DOI: 10.1080 / 00207168408803438

Teuhola, J. và Nevalainen, O. 1982. Hai thuật toán hiệu quả để lấy mẫu ngẫu nhiên mà không cần thay thế. / IJCM /, 11 (2): 127 Lỗi140. DOI: 10.1080 / 00207168208804304

Trong bài báo trước, các tác giả sử dụng bảng băm và cho rằng thuật toán của họ có độ phức tạp O ( s ). Có thêm một thuật toán bảng băm nhanh, sẽ sớm được triển khai trong pqR (khá nhanh R): https://stat.ethz.ch/pipermail/r-devel/2017-Oc/10/075012.html


1

APL, 18 22 byte

{⍵[0]+(1↑⍺)?⍵[1]-⍵[0]}

Khai báo một hàm ẩn danh có hai đối số . là số lượng số ngẫu nhiên bạn muốn, là một vectơ chứa giới hạn dưới và trên, theo thứ tự đó.

a?bchọn asố ngẫu nhiên trong khoảng 0- bmà không cần thay thế. Bằng cách lấy ⍵[1]-⍵[0]chúng tôi có được kích thước phạm vi. Sau đó, chúng tôi chọn số (xem bên dưới) từ phạm vi đó và thêm giới hạn dưới. Trong C, điều này sẽ là

lower + rand() * (upper - lower)

lần mà không thay thế. Dấu ngoặc đơn không cần thiết vì APL hoạt động từ phải sang trái.

Giả sử tôi đã hiểu chính xác các điều kiện, điều này không đạt được tiêu chí 'độ mạnh' bởi vì hàm sẽ thất bại nếu đưa ra các đối số không chính xác (ví dụ: truyền một vectơ thay vì vô hướng như ).

Trong trường hợp đó là một vectơ chứ không phải vô hướng, 1↑⍺lấy phần tử đầu tiên của . Đối với một vô hướng, đây là vô hướng. Đối với một vectơ, đó là yếu tố đầu tiên. Điều này sẽ làm cho chức năng đáp ứng các tiêu chí 'mạnh mẽ'.

Thí dụ:

Input: 100 {⍵[0]+⍺?⍵[1]-⍵[0]} 0 100
Output: 34 10 85 2 46 56 32 8 36 79 77 24 90 70 99 61 0 21 86 50 83 5 23 27 26 98 88 66 58 54 76 20 91 72 71 65 63 15 33 11 96 60 43 55 30 48 73 75 31 13 19 3 45 44 95 57 97 37 68 78 89 14 51 47 74 9 67 18 12 92 6 49 41 4 80 29 82 16 94 52 59 28 17 87 25 84 35 22 38 1 93 81 42 40 69 53 7 39 64 62

2
Đây không phải là một mã golf mà là một cose nhanh nhất, do đó mục tiêu là tạo ra mã nhanh nhất để thực hiện nhiệm vụ thay vì ngắn nhất. Dù sao, bạn không thực sự cần phải chọn các mục từ các đối số như vậy và bạn có thể xác định thứ tự của chúng, do đó {⍵+⍺?⎕-⍵}, đủ để nhắc nhở về giới hạn trên và đối số bên phải bị ràng buộc thấp hơn
Uriel

0

Scala

object RandSet {
  val random = util.Random 

  def rand (count: Int, lower: Int, upper: Int, sofar: Set[Int] = Set.empty): Set[Int] =
    if (count == sofar.size) sofar else 
    rand (count, lower, upper, sofar + (random.nextInt (upper-lower) + lower)) 
}

object RandSetRunner {

  def main (args: Array [String]) : Unit = {
    if (args.length == 4) 
      (0 until args (0).toInt).foreach { unused => 
      println (RandSet.rand (args (1).toInt, args (2).toInt, args (3).toInt).mkString (" "))
    }
    else Console.err.println ("usage: scala RandSetRunner OUTERCOUNT COUNT MIN MAX")
  }
}

biên dịch và chạy:

scalac RandSetRunner.scala 
scala RandSetRunner 200 15 0 100

Dòng thứ hai sẽ chạy 200 thử nghiệm với 15 giá trị từ 0 đến 100, vì Scala tạo ra mã byte nhanh nhưng cần một chút thời gian khởi động. Vì vậy, 200 bắt đầu với 15 giá trị từ 0 đến 100 sẽ tiêu tốn nhiều thời gian hơn.

Mẫu trên Lõi đơn 2 Ghz:

time scala RandSetRunner 100000 10 0 1000000 > /dev/null

real    0m2.728s
user    0m2.416s
sys     0m0.168s

Hợp lý:

Sử dụng các số ngẫu nhiên tích hợp và chọn đệ quy trong phạm vi (tối đa), thêm min và kiểm tra, nếu kích thước của tập hợp là kích thước dự kiến.

Phê bình:

  • Sẽ nhanh chóng cho các mẫu nhỏ của phạm vi lớn, nhưng nếu nhiệm vụ là chọn gần như tất cả các yếu tố của một mẫu (999 số trong số 1000) thì nó sẽ liên tục chọn số, đã có trong bộ.
  • Từ câu hỏi, tôi không chắc chắn, liệu tôi có phải vệ sinh trước các yêu cầu không được thực hiện như Lấy 10 số khác biệt từ 4 đến 8. Điều này sẽ dẫn đến một vòng lặp vô tận, nhưng có thể dễ dàng tránh được bằng một kiểm tra trước mà tôi sẽ thêm vào nếu yêu cầu.

0

Kế hoạch

Không chắc chắn tại sao bạn cần thông qua 3 thông số và tại sao tôi cần phải giả sử bất kỳ phạm vi nào ...

(import srfi-1) ;; for iota
(import srfi-27) ;; randomness
(import srfi-43) ;; for vector-swap!

(define rand (random-source-make-integers
               default-random-source))

;; n: length, i: lower limit
(define (random-range n i)
  (let ([v (list->vector (iota n i))])
    (let f ([n n])
      (let* ([i (rand n)] [n (- n 1)])
        (if (zero? n) v
            (begin (vector-swap! v n i) (f n)))))))

0

R

random <- function(count, from, to) {
  rand.range <- to - from

  vec <- c()

  for (i in 1:count) {
    t <- sample(rand.range, 1) + from
    while(i %in% vec) {
      t <- sample(rand.range, 1) + from
    }
    vec <- c(vec, t)
  }

  return(vec)
}

0

C ++

Mã này là tốt nhất khi vẽ nhiều mẫu từ phạm vi.

#include <exception>
#include <stdexcept>
#include <cstdlib>

template<typename OutputIterator>
 void sample(OutputIterator out, int n, int min, int max)
{
  if (n < 0)
    throw std::runtime_error("negative sample size");
  if (max < min)
    throw std::runtime_error("invalid range");
  if (n > max-min+1)
    throw std::runtime_error("sample size larger than range");

  while (n>0)
  {
    double r = std::rand()/(RAND_MAX+1.0);
    if (r*(max-min+1) < n)
    {
      *out++ = min;
      --n;
    }
    ++min;
  }
}

Điều này có thể dễ dàng bị mắc kẹt trong một vòng lặp vô hạn trừ khi max-minlớn hơn nhiều n. Ngoài ra, chuỗi đầu ra đang tăng đơn điệu, do đó bạn nhận được tính ngẫu nhiên chất lượng rất thấp nhưng vẫn phải trả chi phí gọi rand()nhiều lần cho mỗi kết quả. Một sự xáo trộn ngẫu nhiên của mảng có thể sẽ có giá trị thêm thời gian chạy.
Peter Cordes

0

Q (19 ký tự)

f:{(neg x)?y+til z}

Sau đó sử dụng f [x; y; z] làm [đếm số trong tập đầu ra; điểm bắt đầu; kích thước của phạm vi]

ví dụ: f [5; 10; 10] sẽ tạo ra 5 số ngẫu nhiên riêng biệt trong khoảng từ 10 đến 19.

q)\ts do[100000;f[100;1;10000]]
2418 131456j

Kết quả ở trên cho thấy hiệu suất ở 100.000 lần lặp chọn 100 số ngẫu nhiên trong khoảng từ 1 đến 10.000.


0

R, 31 hoặc 40 byte (tùy thuộc vào ý nghĩa của từ phạm vi phạm vi)

Nếu đầu vào có 3 số, a[1], a[2], a[3]và theo phạm vi của phạm vi, bạn có nghĩa là một chuỗi số nguyên từ [2] đến [3], thì bạn có điều này:

a=scan();sample(a[2]:a[3],a[1])

Nếu bạn có một mảng nmà bạn sắp lấy mẫu lại, nhưng theo giới hạn của giới hạn dưới và trên, như giá trị mẫu lại của mảng của mảng đã cho ntừ phạm vi a[1]...a[2], thì hãy sử dụng:

a=scan();sample(n[n>=a[2]&n<=a[3]],a[1])

Tôi khá ngạc nhiên tại sao kết quả trước đó không được đánh golf khi xem xét mẫu tích hợp với các thiết bị thay thế! Chúng tôi tạo ra một vectơ thỏa mãn điều kiện phạm vi và lấy mẫu lại.

  • Tính mạnh mẽ: các trường hợp góc (các chuỗi có cùng độ dài với phạm vi lấy mẫu từ) được xử lý theo mặc định.
  • Run-time: cực kỳ nhanh vì được tích hợp sẵn.
  • Tính ngẫu nhiên: hạt giống được tự động thay đổi mỗi khi RNG được gọi.

ít nhất là trên máy của tôi, 0:(2^31)gây ra mộtError: cannot allocate a vector of size 16.0 Gb
Giuseppe

@Giuseppe Gần đây, tôi đã làm việc với các vấn đề về bộ nhớ lớn và giải pháp cho vấn đề đó thực sự là ... chạy nó trên một máy tốt hơn. Các hạn chế trong việc xây dựng tác vụ liên quan đến bộ xử lý, không phải cho bộ nhớ, vậy có phải là ... lạm dụng quy tắc? Ah, tôi là một ass. Tôi nghĩ rằng đó là một thử thách golf mã , nhưng thực sự nó là ... mã nhanh nhất. Tôi đoán tôi thua?
Andreï Kostyrka

0

Javascript (sử dụng thư viện bên ngoài) (64 byte / 104 byte ??)

(a,b,n)=>_.Range(0,n).Select(x=>Math.random()*(b-a)+a).ToArray()

Liên kết đến lib: https://github.com/mvegh1/Enumerable/

Giải thích mã: Biểu thức Lambda chấp nhận min, max, được tính là args. Tạo một tập hợp kích thước n và ánh xạ mỗi phần tử thành một số ngẫu nhiên phù hợp với tiêu chí tối thiểu / tối đa. Chuyển đổi sang mảng JS gốc và trả về nó. Tôi cũng chạy cái này với đầu vào có kích thước 5.000.000 và sau khi áp dụng một biến đổi riêng biệt vẫn hiển thị 5.000.000 phần tử. Nếu nó được đồng ý rằng điều này không đủ an toàn để đảm bảo cho sự khác biệt, tôi sẽ cập nhật câu trả lời

Tôi đã bao gồm một số thống kê trong hình ảnh dưới đây ...

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

EDIT: Hình ảnh dưới đây cho thấy mã / hiệu suất đảm bảo mọi yếu tố sẽ khác biệt. Nó chậm hơn nhiều (6,65 giây đối với 50.000 phần tử) so với mã gốc ở trên cho cùng một đối số (0,012 giây)

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


0

K (oK) , 14 byte

Giải pháp:

{y+(-x)?1+z-y}

Hãy thử trực tuyến!

Thí dụ:

> {y+(-x)?1+z-y}. 10 10 20      / note: there are two ways to provide input, dot or
13 20 16 17 19 10 14 12 11 18
> {y+(-x)?1+z-y}[10;10;20]      / explicitly with [x;y;z]
12 11 13 19 15 17 18 20 14 10

Giải trình:

Có 3 đầu vào ngầm cho mỗi thông số:

  • x, số lượng trong bộ đầu ra,
  • y, giới hạn dưới (bao gồm)
  • z, giới hạn trên (bao gồm)

{y+(-x)?1+z-y} / the solution
{            } / lambda function with x, y and z as implicit inputs
          z-y  / subtract lower limit from upper limit
        1+     / add 1
   (-x)?       / take x many distinct items from 0..(1+z=y)
 y+            / add lower limit

Ghi chú:

Ngoài ra một polyglot trong q/kdb+với một bộ dấu ngoặc bổ sung: {y+((-)x)?1+z-y}(16 byte).


0

Tiên đề + thư viện của nó

f(n:PI,a:INT,b:INT):List INT==
    r:List INT:=[]
    a>b or n>99999999 =>r
    d:=1+b-a
    for i in 1..n repeat
          r:=concat(r,a+random(d)$INT)
    r

Hàm f () ở trên trả về là lỗi danh sách trống, trong trường hợp f (n, a, b) với a> b. Trong các trường hợp đầu vào không hợp lệ khác, nó không chạy với một thông báo lỗi trong cửa sổ Axiom, bởi vì đối số sẽ không đúng loại. Ví dụ

(6) -> f(1,1,5)
   (6)  [2]
                                                       Type: List Integer
(7) -> f(1,1,1)
   (7)  [1]
                                                       Type: List Integer
(10) -> f(10,1,1)
   (10)  [1,1,1,1,1,1,1,1,1,1]
                                                       Type: List Integer
(11) -> f(10,-20,-1)
   (11)  [- 10,- 4,- 18,- 5,- 5,- 11,- 15,- 1,- 20,- 1]
                                                       Type: List Integer
(12) -> f(10,-20,-1)
   (12)  [- 4,- 5,- 3,- 4,- 18,- 1,- 2,- 14,- 19,- 8]
                                                       Type: List Integer
(13) -> f(10,-20,-1)
   (13)  [- 18,- 12,- 12,- 19,- 19,- 15,- 5,- 17,- 19,- 4]
                                                       Type: List Integer
(14) -> f(10,-20,-1)
   (14)  [- 8,- 11,- 20,- 10,- 4,- 8,- 11,- 3,- 10,- 16]
                                                       Type: List Integer
(15) -> f(10,9,-1)
   (15)  []
                                                       Type: List Integer
(16) -> f(10,0,100)
   (16)  [72,83,41,35,27,0,33,18,60,38]
                                                       Type: List Integer
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.