June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -1,23 +1,29 @@
This is a method of randomly sampling n items from a set of M items, with equal probability; where M >= n and M, the number of items is unknown until the end.
This means that the equal probability sampling should be maintained for all successive items > n as they become available (although the content of successive samples can change).
;The algorithm:
# Select the first n items as the sample as they become available;
# For the i-th item where i > n, have a random chance of n/i of keeping it. If failing this chance, the sample remains the same. If not, have it randomly (1/n) replace one of the previously selected n items of the sample.
# Repeat #2 for any subsequent items.
:* Select the first n items as the sample as they become available;
:* For the i-th item where i > n, have a random chance of n/i of keeping it. If failing this chance, the sample remains the same. If not, have it randomly (1/n) replace one of the previously selected n items of the sample.
:* Repeat &nbsp; 2<sup>nd</sup> step &nbsp; for any subsequent items.
;The Task:
# Create a function <code>s_of_n_creator</code> that given <math>n</math> the maximum sample size, returns a function <code>s_of_n</code> that takes one parameter, <code>item</code>.
# Function <code>s_of_n</code> when called with successive items returns an equi-weighted random sample of up to n of its items so far, each time it is called, calculated using Knuths Algorithm S.
# Test your functions by printing and showing the frequency of occurrences of the selected digits from 100,000 repetitions of:
:# Use the s_of_n_creator with n == 3 to generate an s_of_n.
:# call s_of_n with each of the digits 0 to 9 in order, keeping the returned three digits of its random sampling from its last call with argument item=9.
:* Create a function <code>s_of_n_creator</code> that given <math>n</math> the maximum sample size, returns a function <code>s_of_n</code> that takes one parameter, <code>item</code>.
:* Function <code>s_of_n</code> when called with successive items returns an equi-weighted random sample of up to n of its items so far, each time it is called, calculated using Knuths Algorithm S.
:* Test your functions by printing and showing the frequency of occurrences of the selected digits from 100,000 repetitions of:
:::# Use the s_of_n_creator with n == 3 to generate an s_of_n.
:::# call s_of_n with each of the digits 0 to 9 in order, keeping the returned three digits of its random sampling from its last call with argument item=9.
Note: A class taking n and generating a callable instance/function might also be used.
;Reference:
* The Art of Computer Programming, Vol 2, 3.4.2 p.142
;Cf.
;Related tasks:
* [[One of n lines in a file]]
* [[Accumulator factory]]
<br><br>

View file

@ -1,35 +1,26 @@
function makesofn(n::Int)
buf = Any[]
function makesofn(n::Integer)
buf = Vector{typeof(n)}(0)
i = 0
function sofn(item)
return function sofn(item)
i += 1
if i <= n
if i n
push!(buf, item)
else
j = rand(1:i)
if j <= n
buf[j] = item
end
if j ≤ n buf[j] = item end
end
return buf
end
return sofn
end
nhist = zeros(Int, 10)
for i in 1:10^5
for _ in 1:10^5
kas = makesofn(3)
for j in 0:8
kas(j)
end
for k in kas(9)
nhist[k+1] += 1
end
for j in 0:8 kas(j) end
for k in kas(9) nhist[k+1] += 1 end
end
println("Simulating sof3(0:9) 100000 times:")
for (i, c) in enumerate(nhist)
println(@sprintf " %2d => %5d" i-1 c)
@printf("%5d → %5d\n", i-1, c)
end

View file

@ -1,40 +1,39 @@
/*REXX program using Knuth's algorithm S (a random sampling N of M items).*/
parse arg trials size . /*obtain optional arguments from the CL*/
if trials=='' then trials=100000 /*Not specified? Then use the default.*/
if size=='' then size= 3 /* " " " " " " */
#.=0 /*initialize frequency counter array. */
do trials /*OK, now let's light this candle. */
call s_of_n_creator size /*create an initial list of N items. */
/*REXX program using Knuth's algorithm S (a random sampling N of M items). */
parse arg trials size . /*obtain optional arguments from the CL*/
if trials=='' | trials=="," then trials=100000 /*Not specified? Then use the default.*/
if size=='' | size=="," then size= 3 /* " " " " " " */
#.=0 /*initialize frequency counter array. */
do trials /*OK, now let's light this candle. */
call s_of_n_creator size /*create an initial list of N items. */
do gener=0 for 10
call s_of_n gener /*call s_of_n with a single decimal dig*/
end /*gener*/
do gener=0 for 10
call s_of_n gener /*call s_of_n with a single decimal dig*/
end /*gener*/
do count=1 for size /*let's examine what SofN generated. */
_=!.count /*get a decimal digit from the Nth */
#._=#._+1 /* ··· item, and count it, of course.*/
end /*count*/
end /*trials*/
@='trials, and with size='
say "Using Knuth's algorithm S for" commas(trials) @ || commas(size)":"
say
do dig=0 to 9 /* [↓] display the frequency of a dig.*/
say left('',20) "frequency of the" dig 'digit is:' commas(#.dig)
end /*dig*/
exit /*stick a fork in it, we're all done. */
/*────────────────────────────────────────────────────────────────────────────*/
commas: procedure; parse arg _; n=_'.9'; #=123456789; b=verify(n,#,"M")
e=verify(n, #'0', , verify(n, #"0.", 'M')) - 4
do j=e to b by -3; _=insert(',',_,j); end /*j*/; return _
/*────────────────────────────────────────────────────────────────────────────*/
s_of_n: parse arg item; items=items+1 /*get "item", bump the items counter.*/
c=random(1, items) /* [↓] should replace a previous item?*/
if c>size then return /*probability isn't good, so skip it. */
_=random(1, size); !._=item /*now, figure out which previous ··· */
return /* ··· item to replace with ITEM.*/
/*────────────────────────────────────────────────────────────────────────────*/
s_of_n_creator: parse arg item 1 items /*generate ITEM number of items. */
do k=1 for item /*traipse through the first N items. */
!.k=random(0, 9) /*set the Kth item with random digit.*/
end /*k*/
return /*the piddly stuff is done (for now). */
/* [↓] examine what SofN generated. */
do count=1 for size; _= !.count /*get a dec. digit from the Nth item. */
#._=#._ + 1 /*bump counter for the decimal digit. */
end /*count*/
end /*trials*/
@= ' trials, and with a size of '
hdr=" Using Knuth's algorithm S for " commas(trials) @ || commas(size)": "
say hdr; say copies("", length(hdr) ) /*display the header and its separator.*/
do dig=0 to 9 /* [↓] display the frequency of a dig.*/
say left('', 20) "frequency of the" dig 'digit is: ' commas(#.dig)
end /*dig*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg _; do jc=length(_)-3 to 1 by -3; _=insert(',', _, jc); end; return _
/*──────────────────────────────────────────────────────────────────────────────────────*/
s_of_n: parse arg item; items=items + 1 /*get "item", bump the items counter.*/
c=random(1, items) /* [↓] should replace a previous item?*/
if c>size then return /*probability isn't good, so skip it. */
_=random(1, size); !._=item /*now, figure out which previous ··· */
return /* ··· item to replace with ITEM.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
s_of_n_creator: parse arg item 1 items /*generate ITEM number of items. */
do k=1 for item /*traipse through the first N items. */
!.k=random(0, 9) /*set the Kth item with random digit.*/
end /*k*/
return /*the piddly stuff is done (for now). */

View file

@ -0,0 +1,46 @@
use rand::{Rng,weak_rng};
struct SofN<R: Rng+Sized, T> {
rng: R,
sample: Vec<T>,
i: usize,
n: usize,
}
impl<R: Rng, T> SofN<R, T> {
fn new(rng: R, n: usize) -> Self {
SofN{rng, sample: Vec::new(), i: 0, n}
}
fn add(&mut self, item: T) {
self.i += 1;
if self.i <= self.n {
self.sample.push(item);
} else if self.rng.gen_range(0, self.i) < self.n {
self.sample[self.rng.gen_range(0, self.n)] = item;
}
}
fn sample(&self) -> &Vec<T> {
&self.sample
}
}
pub fn main() {
const MAX: usize = 10;
let mut bin: [i32; MAX] = Default::default();
for _ in 0..100000 {
let mut s_of_n = SofN::new(weak_rng(), 3);
for i in 0..MAX { s_of_n.add(i); }
for s in s_of_n.sample() {
bin[*s] += 1;
}
}
for (i, x) in bin.iter().enumerate() {
println!("frequency of {}: {}", i, x);
}
}

View file

@ -0,0 +1,21 @@
import java.util
import scala.util.Random
object KnuthsAlgorithmS extends App {
import scala.collection.JavaConverters._
val (n, rand, bin) = (3, Random, new Array[Int](10))
for (_ <- 0 until 100000) {
val sample = new util.ArrayList[Int](n)
for (item <- 0 until 10) {
if (item < n) sample.add(item)
else if (rand.nextInt(item + 1) < n)
sample.asScala(rand.nextInt(n)) = item
}
for (s <- sample.asScala.toList) bin(s) += 1
}
println(bin.mkString("[", ", ", "]"))
}