capricious 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +5 -0
- data/VERSION +1 -1
- data/lib/capricious.rb +2 -0
- data/lib/capricious/generic_prng.rb +53 -0
- data/lib/capricious/lfsr.rb +4 -3
- data/lib/capricious/mwc5.rb +71 -0
- data/lib/capricious/poisson.rb +46 -0
- data/lib/capricious/sample_sink.rb +4 -4
- data/lib/capricious/uniform.rb +5 -18
- data/spec/poisson_spec.rb +27 -0
- data/spec/uniform_spec.rb +19 -19
- metadata +7 -2
data/CHANGES
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/lib/capricious.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# capricious/generic_prng.rb: generic PRNG mixin with selectable source-randomness
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'capricious/lfsr'
|
20
|
+
require 'capricious/mwc5'
|
21
|
+
require 'capricious/sample_sink'
|
22
|
+
|
23
|
+
module Capricious
|
24
|
+
module PRNG
|
25
|
+
|
26
|
+
def initialize(seed=nil, policy=MWC5, keep_stats=false)
|
27
|
+
prng_initialize(seed, policy, keep_stats)
|
28
|
+
end
|
29
|
+
|
30
|
+
def prng_initialize(seed=nil, policy=MWC5, keep_stats=false)
|
31
|
+
@prng = policy.new_with_seed(seed)
|
32
|
+
@seed = @prng.seed
|
33
|
+
@aggregate = SampleSink.new if keep_stats
|
34
|
+
end
|
35
|
+
|
36
|
+
def next
|
37
|
+
val = next_value
|
38
|
+
@aggregate << val if @aggregate
|
39
|
+
val
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset(seed=nil)
|
43
|
+
@prng.reset(seed)
|
44
|
+
@aggregate = SampleSink.new if @aggregate
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.included(base)
|
48
|
+
base.class_eval do
|
49
|
+
attr_reader :aggregate, :seed
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/capricious/lfsr.rb
CHANGED
@@ -47,13 +47,13 @@ module Capricious
|
|
47
47
|
reset
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
50
|
+
def next_i
|
51
51
|
shift_reg
|
52
52
|
@reg
|
53
53
|
end
|
54
54
|
|
55
55
|
def next_f
|
56
|
-
|
56
|
+
next_i
|
57
57
|
@reg.quo(@ns::MASK.to_f)
|
58
58
|
end
|
59
59
|
|
@@ -66,7 +66,8 @@ module Capricious
|
|
66
66
|
module SixtyFourBitShifter
|
67
67
|
MASK = 0xffffffffffffffff
|
68
68
|
SIZE = 64
|
69
|
-
BITS = [64,63,61,60]
|
69
|
+
# BITS = [64,63,61,60]
|
70
|
+
BITS = [64,4,3,1]
|
70
71
|
BITSELECT = BITS.map {|bit| "@reg[#{SIZE-bit}]"}.join("^")
|
71
72
|
|
72
73
|
class_eval <<-SHIFT_REG
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# capricious/mwc5.rb: 32-bit multiply-with-carry PRNG
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
# Algorithm due to George Marsaglia: http://groups.google.com/group/comp.lang.c/msg/e3c4ea1169e463ae
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
|
20
|
+
require 'capricious/lfsr'
|
21
|
+
|
22
|
+
module Capricious
|
23
|
+
class MWC5
|
24
|
+
attr_reader :seed, :seeds
|
25
|
+
def MWC5.new_with_seed(seed)
|
26
|
+
MWC5.new(seed)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(seed=nil)
|
30
|
+
reset(seed)
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset(seed=nil)
|
34
|
+
@seed ||= (seed || Time.now.utc.to_i)
|
35
|
+
unless @seeds
|
36
|
+
seeder = LFSR.new_with_seed(@seed)
|
37
|
+
@seeds = [seeder.next_i & 0xffffffff]
|
38
|
+
4.times do
|
39
|
+
9.times do
|
40
|
+
# the observed lag-10 autocorrelation of the LFSRs is low
|
41
|
+
seeder.next_i
|
42
|
+
end
|
43
|
+
@seeds << (seeder.next_i & 0xffffffff)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@x,@y,@z,@w,@v = @seeds
|
48
|
+
end
|
49
|
+
|
50
|
+
def next_i
|
51
|
+
shift_ks
|
52
|
+
end
|
53
|
+
|
54
|
+
def next_f
|
55
|
+
next_i.quo(0xffffffff.to_f)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def shift_ks
|
60
|
+
# XXX: this is ugly, but it has to be to avoid coercing things into bignums
|
61
|
+
t=(@x^(@x>>7)) & 0xffffffff
|
62
|
+
@x=@y
|
63
|
+
@y=@z
|
64
|
+
@z=@w
|
65
|
+
@w=@v
|
66
|
+
@v=(@v^(@v<<6))^(t^(t<<13)) & 0xffffffff
|
67
|
+
yy = ((@y & 0x7fffffff << 1) + 1) & 0xffffffff
|
68
|
+
(yy * @v) & 0xffffffff;
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# capricious/poisson.rb: Poisson-distribution PRNG, with selectable source-randomness policy
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton <willb@redhat.com>
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'capricious/generic_prng'
|
20
|
+
|
21
|
+
module Capricious
|
22
|
+
class Poisson
|
23
|
+
include PRNG
|
24
|
+
|
25
|
+
attr_reader :z
|
26
|
+
|
27
|
+
def initialize(l, seed=nil, policy=MWC5, keep_stats=false)
|
28
|
+
@z = Math.exp(-l)
|
29
|
+
prng_initialize(seed, policy, keep_stats)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# Algorithm 369, CACM, January 1970
|
34
|
+
def next_value
|
35
|
+
k = 0
|
36
|
+
|
37
|
+
t = @prng.next_f
|
38
|
+
while t > self.z
|
39
|
+
k = k + 1
|
40
|
+
t = t * @prng.next_f
|
41
|
+
end
|
42
|
+
|
43
|
+
k
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -19,13 +19,13 @@
|
|
19
19
|
module Capricious
|
20
20
|
class SampleSink
|
21
21
|
|
22
|
-
attr_reader :min, :max, :count, :mean
|
22
|
+
attr_reader :min, :max, :count, :mean, :variance
|
23
23
|
|
24
24
|
def initialize
|
25
25
|
@min = nil
|
26
26
|
@max = nil
|
27
27
|
@count = 0
|
28
|
-
@
|
28
|
+
@variance = 0.0
|
29
29
|
@mean = 0.0
|
30
30
|
@sum_x2 = 0.0
|
31
31
|
end
|
@@ -42,7 +42,7 @@ module Capricious
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def stddev
|
45
|
-
Math::sqrt(@
|
45
|
+
Math::sqrt(@variance)
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
@@ -56,7 +56,7 @@ module Capricious
|
|
56
56
|
dev = sample - @mean
|
57
57
|
@mean += (dev / @count)
|
58
58
|
@sum_x2 += (dev * (sample - @mean))
|
59
|
-
@
|
59
|
+
@variance = @sum_x2 / @count
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
data/lib/capricious/uniform.rb
CHANGED
@@ -16,28 +16,15 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
|
-
require 'capricious/
|
20
|
-
require 'capricious/sample_sink'
|
19
|
+
require 'capricious/generic_prng'
|
21
20
|
|
22
21
|
module Capricious
|
23
22
|
class Uniform
|
24
|
-
|
23
|
+
include PRNG
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
@
|
29
|
-
@aggregate = SampleSink.new if keep_stats
|
30
|
-
end
|
31
|
-
|
32
|
-
def next
|
33
|
-
val = @prng.next_f
|
34
|
-
@aggregate << val if @aggregate
|
35
|
-
val
|
36
|
-
end
|
37
|
-
|
38
|
-
def reset(seed=nil)
|
39
|
-
@prng.reset(seed)
|
40
|
-
@aggregate = SampleSink.new if @aggregate
|
25
|
+
private
|
26
|
+
def next_value
|
27
|
+
@prng.next_f
|
41
28
|
end
|
42
29
|
end
|
43
30
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
module Capricious
|
4
|
+
LAMBDA = 8
|
5
|
+
SAMPLE_COUNT = 200
|
6
|
+
|
7
|
+
describe Poisson do
|
8
|
+
def generate_samples(count=SAMPLE_COUNT)
|
9
|
+
count.times {@poisson.next}
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@poisson = Capricious::Poisson.new(LAMBDA, nil, MWC5, true)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should generate distributed numbers in a Poisson distribution with lambda #{LAMBDA}, as judged by mean and variance estimates" do
|
17
|
+
generate_samples(10000)
|
18
|
+
@poisson.aggregate.mean.should be_close(LAMBDA, 0.05)
|
19
|
+
@poisson.aggregate.variance.should be_close(LAMBDA, 0.15)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should generate the same sequence given the same seed" do
|
23
|
+
@poisson2 = Poisson.new(LAMBDA, @poisson.seed, MWC5, true)
|
24
|
+
SAMPLE_COUNT.times { @poisson2.next.should == @poisson.next }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/uniform_spec.rb
CHANGED
@@ -3,32 +3,32 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
module Capricious
|
4
4
|
EXPECTED_MEAN = 0.5
|
5
5
|
EXPECTED_STDDEV = 0.288675134594813
|
6
|
-
SAMPLE_COUNT =
|
6
|
+
SAMPLE_COUNT = 20000
|
7
7
|
|
8
8
|
describe Uniform do
|
9
|
-
def generate_samples(count=SAMPLE_COUNT)
|
9
|
+
def generate_samples(policy=MWC5, count=SAMPLE_COUNT)
|
10
|
+
@uniform = Capricious::Uniform.new(nil, policy, true)
|
10
11
|
count.times {@uniform.next}
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@uniform.aggregate.max.should <= 1.0
|
20
|
-
@uniform.aggregate.min.should > 0.0
|
21
|
-
end
|
14
|
+
[LFSR,MWC5].each do |policy|
|
15
|
+
it "should, given policy #{policy.name}, generate numbers in the range (0,1]" do
|
16
|
+
generate_samples(policy)
|
17
|
+
@uniform.aggregate.max.should <= 1.0
|
18
|
+
@uniform.aggregate.min.should > 0.0
|
19
|
+
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
it "should, given policy #{policy.name}, generate uniformly-distributed numbers in the range (0,1], as judged by mean and variance estimates" do
|
22
|
+
generate_samples(policy)
|
23
|
+
@uniform.aggregate.mean.should be_close(EXPECTED_MEAN, 0.01)
|
24
|
+
@uniform.aggregate.stddev.should be_close(EXPECTED_STDDEV, 0.01)
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
it "should, given policy #{policy.name}, generate the same sequence given the same seed" do
|
28
|
+
@uniform = Uniform.new(nil, policy, false)
|
29
|
+
@uniform2 = Uniform.new(nil, policy, false)
|
30
|
+
(SAMPLE_COUNT/10).times { @uniform2.next.should == @uniform.next }
|
31
|
+
end
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capricious
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Benton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-03-
|
12
|
+
date: 2010-03-12 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,9 +40,13 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- VERSION
|
42
42
|
- lib/capricious.rb
|
43
|
+
- lib/capricious/generic_prng.rb
|
43
44
|
- lib/capricious/lfsr.rb
|
45
|
+
- lib/capricious/mwc5.rb
|
46
|
+
- lib/capricious/poisson.rb
|
44
47
|
- lib/capricious/sample_sink.rb
|
45
48
|
- lib/capricious/uniform.rb
|
49
|
+
- spec/poisson_spec.rb
|
46
50
|
- spec/spec.opts
|
47
51
|
- spec/spec_helper.rb
|
48
52
|
- spec/uniform_spec.rb
|
@@ -75,5 +79,6 @@ signing_key:
|
|
75
79
|
specification_version: 3
|
76
80
|
summary: Pseudorandom number generator classes and support code
|
77
81
|
test_files:
|
82
|
+
- spec/poisson_spec.rb
|
78
83
|
- spec/spec_helper.rb
|
79
84
|
- spec/uniform_spec.rb
|