2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -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>

View 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)))

View file

@ -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)

View 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)));;
}
}
}

View file

@ -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{*} ...^ { (%){$_}++ } }

View file

@ -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
}

View file

@ -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' )
}
}

View file

@ -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

View 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
}