2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -1,9 +1,12 @@
|
|||
Let <code>f</code> be a uniformly-randomly chosen mapping from the numbers 1..N to the numbers 1..N (note: not necessarily a permutation of 1..N; the mapping could produce a number in more than one way or not at all). At some point, the sequence <code>1, f(1), f(f(1))...</code> will contain a <em>repetition</em>, a number that occurring for the second time in the sequence.
|
||||
|
||||
|
||||
;Task:
|
||||
Write a program or a script that estimates, for each <code>N</code>, the average length until the first such repetition.
|
||||
|
||||
Also calculate this expected length using an analytical formula, and optionally compare the simulated result with the theoretical one.
|
||||
|
||||
|
||||
This problem comes from the end of Donald Knuth's [http://www.youtube.com/watch?v=cI6tt9QfRdo Christmas tree lecture 2011].
|
||||
|
||||
Example of expected output:
|
||||
|
|
@ -30,3 +33,4 @@ Example of expected output:
|
|||
18 4.9951 5.0071 ( 0.24%)
|
||||
19 5.1312 5.1522 ( 0.41%)
|
||||
20 5.2699 5.2936 ( 0.45%)</pre>
|
||||
<br>
|
||||
|
|
|
|||
44
Task/Average-loop-length/Clojure/average-loop-length.clj
Normal file
44
Task/Average-loop-length/Clojure/average-loop-length.clj
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
(ns cyclelengths
|
||||
(:gen-class))
|
||||
|
||||
(defn factorial [n]
|
||||
" n! "
|
||||
(apply *' (range 1 (inc n)))) ; Use *' (vs. *) to allow arbitrary length arithmetic
|
||||
|
||||
(defn pow [n i]
|
||||
" n^i"
|
||||
(apply *' (repeat i n)))
|
||||
|
||||
(defn analytical [n]
|
||||
" Analytical Computation "
|
||||
(->>(range 1 (inc n))
|
||||
(map #(/ (factorial n) (pow n %) (factorial (- n %)))) ;calc n %))
|
||||
(reduce + 0)))
|
||||
|
||||
;; Number of random times to test each n
|
||||
(def TIMES 1000000)
|
||||
|
||||
(defn single-test-cycle-length [n]
|
||||
" Single random test of cycle length "
|
||||
(loop [count 0
|
||||
bits 0
|
||||
x 1]
|
||||
(if (zero? (bit-and x bits))
|
||||
(recur (inc count) (bit-or bits x) (bit-shift-left 1 (rand-int n)))
|
||||
count)))
|
||||
|
||||
(defn avg-cycle-length [n times]
|
||||
" Average results of single tests of cycle lengths "
|
||||
(/
|
||||
(reduce +
|
||||
(for [i (range times)]
|
||||
(single-test-cycle-length n)))
|
||||
times))
|
||||
|
||||
;; Show Results
|
||||
(println "\tAvg\t\tExp\t\tDiff")
|
||||
(doseq [q (range 1 21)
|
||||
:let [anal (double (analytical q))
|
||||
avg (double (avg-cycle-length q TIMES))
|
||||
diff (Math/abs (* 100 (- 1 (/ avg anal))))]]
|
||||
(println (format "%3d\t%.4f\t%.4f\t%.2f%%" q avg anal diff)))
|
||||
|
|
@ -2,20 +2,18 @@ defmodule RC do
|
|||
def factorial(0), do: 1
|
||||
def factorial(n), do: Enum.reduce(1..n, 1, &(&1 * &2))
|
||||
|
||||
def loop_length(n), do: loop_length(n, HashSet.new)
|
||||
def loop_length(n), do: loop_length(n, MapSet.new)
|
||||
|
||||
defp loop_length(n, set) do
|
||||
r = :random.uniform(n)
|
||||
if Set.member?(set, r), do: Set.size(set),
|
||||
else: loop_length(n, Set.put(set, r))
|
||||
r = :rand.uniform(n)
|
||||
if r in set, do: MapSet.size(set), else: loop_length(n, MapSet.put(set, r))
|
||||
end
|
||||
|
||||
def task(runs) do
|
||||
IO.puts " N average analytical (error) "
|
||||
IO.puts "=== ========= ========== ========="
|
||||
Enum.each(1..20, fn n ->
|
||||
sum_of_runs = Enum.reduce(1..runs, 0, fn _,sum -> sum + loop_length(n) end)
|
||||
avg = sum_of_runs / runs
|
||||
avg = Enum.reduce(1..runs, 0, fn _,sum -> sum + loop_length(n) end) / runs
|
||||
analytical = Enum.reduce(1..n, 0, fn i,sum ->
|
||||
sum + (factorial(n) / :math.pow(n, i) / factorial(n-i))
|
||||
end)
|
||||
|
|
@ -24,5 +22,5 @@ defmodule RC do
|
|||
end
|
||||
end
|
||||
|
||||
runs = 100_000
|
||||
runs = 1_000_000
|
||||
RC.task(runs)
|
||||
|
|
|
|||
51
Task/Average-loop-length/Java/average-loop-length.java
Normal file
51
Task/Average-loop-length/Java/average-loop-length.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import java.util.ArrayList;
|
||||
|
||||
public class AverageLoopLength {
|
||||
private static final int N = 100000;
|
||||
//analytical(n) = sum_(i=1)^n (n!/(n-i)!/n**i)
|
||||
public static float analytical(int n){
|
||||
float[] factorial = new float[n+1];
|
||||
float[] powers = new float[n+1];
|
||||
factorial[0] = powers[0] = 1;
|
||||
for(int i=1;i<=n;i++){
|
||||
factorial[i] = factorial[i-1] * i;
|
||||
powers[i] = powers[i-1] * n;
|
||||
}
|
||||
float sum = 0;
|
||||
//memoized factorial and powers
|
||||
for(int i=1;i<=n;i++){
|
||||
sum += factorial[n]/factorial[n-i]/powers[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
public static float average(int n){
|
||||
float sum = 0;
|
||||
for(int a=0;a<N;a++){
|
||||
int[] random = new int[n];
|
||||
for(int i=0;i<n;i++){
|
||||
random[i] = (int)(Math.random()*n);
|
||||
}
|
||||
ArrayList<Integer> seen = new ArrayList<>(n);
|
||||
int current = 0;
|
||||
int length = 0;
|
||||
while(true){
|
||||
length++;
|
||||
seen.add(current);
|
||||
current = random[current];
|
||||
if(seen.contains(current)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
sum += length;
|
||||
}
|
||||
return sum/N;
|
||||
}
|
||||
public static void main(String args[]){
|
||||
System.out.println(" N average analytical (error)\n=== ========= ============ =========");
|
||||
for(int i=1;i<=20;i++){
|
||||
float avg = average(i);
|
||||
float ana = analytical(i);
|
||||
System.out.println(String.format("%3d %9.4f %12.4f (%6.2f%%)",i,avg,ana,((ana-avg)/ana*100)));;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ constant TRIALS = 100;
|
|||
for 1 .. MAX_N -> $N {
|
||||
my $empiric = TRIALS R/ [+] find-loop(random-mapping($N)).elems xx TRIALS;
|
||||
my $theoric = [+]
|
||||
map -> $k { $N ** ($k + 1) R/ [*] $k**2, $N - $k + 1 .. $N }, 1 .. $N;
|
||||
map -> $k { $N ** ($k + 1) R/ [*] flat $k**2, $N - $k + 1 .. $N }, 1 .. $N;
|
||||
|
||||
FIRST say " N empiric theoric (error)";
|
||||
FIRST say "=== ========= ============ =========";
|
||||
|
|
@ -15,4 +15,4 @@ for 1 .. MAX_N -> $N {
|
|||
}
|
||||
|
||||
sub random-mapping { hash .list Z=> .roll given ^$^size }
|
||||
sub find-loop { 0, %^mapping{*} ...^ { (state %){$_}++ } }
|
||||
sub find-loop { 0, | %^mapping{*} ...^ { (%){$_}++ } }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
function Get-AnalyticalLoopAverage ( [int]$N )
|
||||
{
|
||||
# Expected loop average = sum from i = 1 to N of N! / (N-i)! / N^(N-i+1)
|
||||
# Equivalently, Expected loop average = sum from i = 1 to N of F(i)
|
||||
# where F(N) = 1, and F(i) = F(i+1)*i/N
|
||||
|
||||
$LoopAverage = $Fi = 1
|
||||
|
||||
If ( $N -eq 1 ) { return $LoopAverage }
|
||||
|
||||
ForEach ( $i in ($N-1)..1 )
|
||||
{
|
||||
$Fi *= $i / $N
|
||||
$LoopAverage += $Fi
|
||||
}
|
||||
return $LoopAverage
|
||||
}
|
||||
|
||||
function Get-ExperimentalLoopAverage ( [int]$N, [int]$Tests = 100000 )
|
||||
{
|
||||
If ( $N -eq 1 ) { return 1 }
|
||||
|
||||
# Using 0 through N-1 instead of 1 through N for speed and simplicity
|
||||
$NMO = $N - 1
|
||||
|
||||
# Create array to hold mapping function
|
||||
$F = New-Object int[] ( $N )
|
||||
|
||||
$Count = 0
|
||||
$Random = New-Object System.Random
|
||||
|
||||
ForEach ( $Test in 1..$Tests )
|
||||
{
|
||||
# Map each number to a random number
|
||||
ForEach ( $i in 0..$NMO )
|
||||
{
|
||||
$F[$i] = $Random.Next( $N )
|
||||
}
|
||||
|
||||
# For each number...
|
||||
ForEach ( $i in 0..$NMO )
|
||||
{
|
||||
# Add the number to the list
|
||||
$List = @()
|
||||
$Count++
|
||||
$List += $X = $i
|
||||
|
||||
# If loop does not yet exist in list...
|
||||
While ( $F[$X] -notin $List )
|
||||
{
|
||||
# Go to the next mapped number and add it to the list
|
||||
$Count++
|
||||
$List += $X = $F[$X]
|
||||
}
|
||||
}
|
||||
}
|
||||
$LoopAvereage = $Count / $N / $Tests
|
||||
return $LoopAvereage
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Display results for N = 1 through 20
|
||||
ForEach ( $N in 1..20 )
|
||||
{
|
||||
$AnalyticalAverage = Get-AnalyticalLoopAverage $N
|
||||
$ExperimentalAverage = Get-ExperimentalLoopAverage $N
|
||||
[pscustomobject] @{
|
||||
N = $N.ToString().PadLeft( 2, ' ' )
|
||||
Analytical = $AnalyticalAverage.ToString( '0.00000000' )
|
||||
Experimental = $ExperimentalAverage.ToString( '0.00000000' )
|
||||
'Error (%)' = ( [math]::Abs( $AnalyticalAverage - $ExperimentalAverage ) / $AnalyticalAverage * 100 ).ToString( '0.00000000' )
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +1,36 @@
|
|||
/*REXX pgm computes average loop length mapping a random field 1..N ───► 1..N */
|
||||
parse arg runs tests seed . /*obtain optional arguments from C.L. */
|
||||
if runs ==',' | runs =='' then runs = 40 /*number of runs. */
|
||||
if tests ==',' | tests =='' then tests= 1000000 /* " " trials. */
|
||||
if seed\==',' & seed\=='' then call random ,,seed /*RAND repeatability?*/
|
||||
numeric digits 100000; !.=0; !.0=1 /*be able to calculate 25,000! */
|
||||
numeric digits max(9,length(!(runs))) /*set the NUMERIC DIGITS for !(runs). */
|
||||
say right( runs, 24) 'runs' /*display number of runs we're using.*/
|
||||
say right( tests, 24) 'tests' /* " " " tests " " */
|
||||
say right( digits(), 24) 'digits' /* " " " digits " " */
|
||||
/*REXX program computes the average loop length mapping a random field 1···N ───► 1···N */
|
||||
parse arg runs tests seed . /*obtain optional arguments from the CL*/
|
||||
if runs =='' | runs =="," then runs = 40 /*Not specified? Then use the default.*/
|
||||
if tests =='' | tests =="," then tests= 1000000 /* " " " " " " */
|
||||
if datatype(seed,'W') then call random ,, seed /*Is integer? For RAND repeatability.*/
|
||||
!.=0; !.0=1 /*used for factorial (!) memoization.*/
|
||||
numeric digits 100000 /*be able to calculate 25k! if need be.*/
|
||||
numeric digits max(9, length( !(runs) ) ) /*set the NUMERIC DIGITS for !(runs). */
|
||||
say right( runs, 24) 'runs' /*display number of runs we're using.*/
|
||||
say right( tests, 24) 'tests' /* " " " tests " " */
|
||||
say right( digits(), 24) 'digits' /* " " " digits " " */
|
||||
say
|
||||
say ' N average exact % error' /*◄──title,header►───┐*/
|
||||
h= ' ─── ───────── ───────── ─────────'; pad=left('',3) /*◄──────┘*/
|
||||
say h
|
||||
do #=1 for runs; ##=right(#,9) /*## is used for indenting the output.*/
|
||||
avg=fmtD(exact(#)) /*use four digits past decimal point. */
|
||||
exa=fmtD(exper(#)) /* " " " " " " */
|
||||
err=fmtD(abs(exa-avg)*100/avg) /* " " " " " " */
|
||||
say ## pad exa pad avg pad err /*display a line of statistics to term.*/
|
||||
end /*#*/
|
||||
say h /*display the final header (some bars).*/
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*────────────────────────────────────────────────────────────────────────────*/
|
||||
!: procedure expose !.; parse arg z; if !.z\==0 then return !.z
|
||||
!=1; do j=1 for z; !=!*j; !.j=!; end; /*factorial*/ return !
|
||||
/*────────────────────────────────────────────────────────────────────────────*/
|
||||
exact: parse arg x; s=0; do j=1 for x; s=s+!(x)/!(x-j)/x**j; end; return s
|
||||
/*────────────────────────────────────────────────────────────────────────────*/
|
||||
exper: parse arg n; k=0; do tests; $.=0 /*do it TESTS times.*/
|
||||
say " N average exact % error " /* ◄─── title, header ►────────┐ */
|
||||
hdr=" ═══ ═════════ ═════════ ═════════"; pad=left('',3) /* ◄────────┘ */
|
||||
say hdr
|
||||
do #=1 for runs; av=fmtD(exact(#)) /*use four digits past decimal point. */
|
||||
xa=fmtD(exper(#)) /* " " " " " " */
|
||||
say right(#,9) pad xa pad av pad fmtD(abs(xa-av)*100/av) /*display values.*/
|
||||
end /*#*/
|
||||
say hdr /*display the final header (some bars).*/
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
!: procedure expose !.; parse arg z; if !.z\==0 then return !.z
|
||||
!=1; do j=2 for z-1; !=!*j; !.j=!; end; /*factorial*/ return !
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
exact: parse arg x; s=0; do j=1 for x; s=s+!(x)/!(x-j)/x**j; end; return s
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
exper: parse arg n; k=0; do tests; $.=0 /*do it TESTS times.*/
|
||||
do n; r=random(1,n); if $.r then leave
|
||||
$.r=1; k=k+1 /*bump the counter. */
|
||||
$.r=1; k=k+1 /*bump the counter. */
|
||||
end /*n*/
|
||||
end /*tests*/
|
||||
end /*tests*/
|
||||
return k/tests
|
||||
/*────────────────────────────────────────────────────────────────────────────*/
|
||||
fmtD: parse arg y,d; d=word(d 4,1); y=format(y,,d); parse var y w '.' f
|
||||
if f=0 then return w || left('', d+1); return y
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
fmtD: parse arg y,d; d=word(d 4,1); y=format(y,,d); parse var y w '.' f
|
||||
if f=0 then return w || left('', d+1); return y
|
||||
|
|
|
|||
74
Task/Average-loop-length/Rust/average-loop-length.rust
Normal file
74
Task/Average-loop-length/Rust/average-loop-length.rust
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
extern crate rand;
|
||||
|
||||
use rand::{ThreadRng, thread_rng};
|
||||
use rand::distributions::{IndependentSample, Range};
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::process;
|
||||
|
||||
fn help() {
|
||||
println!("usage: average_loop_length <max_N> <trials>");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut max_n: u32 = 20;
|
||||
let mut trials: u32 = 1000;
|
||||
|
||||
match args.len() {
|
||||
1 => {}
|
||||
3 => {
|
||||
max_n = args[1].parse::<u32>().unwrap();
|
||||
trials = args[2].parse::<u32>().unwrap();
|
||||
}
|
||||
_ => {
|
||||
help();
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
println!(" N average analytical (error)");
|
||||
println!("=== ========= ============ =========");
|
||||
for n in 1..(max_n + 1) {
|
||||
let the_analytical = analytical(n);
|
||||
let the_empirical = empirical(n, trials, &mut rng);
|
||||
println!(" {:>2} {:3.4} {:3.4} ( {:>+1.2}%)",
|
||||
n,
|
||||
the_empirical,
|
||||
the_analytical,
|
||||
100f64 * (the_empirical / the_analytical - 1f64));
|
||||
}
|
||||
}
|
||||
|
||||
fn factorial(n: u32) -> f64 {
|
||||
(1..n + 1).fold(1f64, |p, n| p * n as f64)
|
||||
}
|
||||
|
||||
fn analytical(n: u32) -> f64 {
|
||||
let sum: f64 = (1..(n + 1))
|
||||
.map(|i| factorial(n) / (n as f64).powi(i as i32) / factorial(n - i))
|
||||
.fold(0f64, |a, v| a + v);
|
||||
sum
|
||||
}
|
||||
|
||||
fn empirical(n: u32, trials: u32, rng: &mut ThreadRng) -> f64 {
|
||||
let sum: f64 = (0..trials)
|
||||
.map(|_t| {
|
||||
let mut item = 1u32;
|
||||
let mut seen = HashSet::new();
|
||||
let range = Range::new(1u32, n + 1);
|
||||
|
||||
for step in 0..n {
|
||||
if seen.contains(&item) {
|
||||
return step as f64;
|
||||
}
|
||||
seen.insert(item);
|
||||
item = range.ind_sample(rng);
|
||||
}
|
||||
n as f64
|
||||
})
|
||||
.fold(0f64, |a, v| a + v);
|
||||
sum / trials as f64
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue