weighted_shuffle 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,5 @@ pkg/
3
3
  bin/
4
4
  .bundle/
5
5
  coverage/
6
+ *.gem
7
+ Gemfile.lock
@@ -2,13 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - ruby-head
6
5
  - jruby-19mode
7
- - jruby-head
8
- - rbx-19mode
9
- - rbx-head
6
+ - rbx-2.1.1
10
7
  script: bundle exec rspec
11
- matrix:
12
- allow_failures:
13
- - rvm: rbx-19mode
14
- - rvm: rbx-head
data/README.md CHANGED
@@ -39,6 +39,10 @@ integrated into ruby Array:
39
39
  array.weighted_shuffle
40
40
  => [:b, :a]
41
41
 
42
+ or with factor
43
+
44
+ array.weighted_shuffle factor: 3
45
+
42
46
  ## Contributing
43
47
 
44
48
  1. Fork it
@@ -1,6 +1,6 @@
1
1
  class Array
2
2
 
3
- def weighted_shuffle
4
- WeightedShuffle::Dealer.new(self).weighted_shuffle
3
+ def weighted_shuffle(options = {})
4
+ WeightedShuffle::Dealer.new(self).weighted_shuffle options
5
5
  end
6
6
  end
@@ -3,13 +3,17 @@ require 'active_support/core_ext/enumerable'
3
3
  module WeightedShuffle
4
4
  class Dealer < Struct.new(:array)
5
5
 
6
- def weighted_shuffle
6
+ def weighted_shuffle(options = {})
7
+ factor = options.delete(:factor)
7
8
  a = Marshal.load Marshal.dump(array)
8
- sum = a.sum { |k,v| v || 0.0 }
9
+ unless factor.nil?
10
+ a.map! { |k, v| [k, v**factor] }
11
+ end
12
+ sum = a.sum { |k, v| v || 0.0 }
9
13
  b = []
10
14
  a.length.times do
11
15
  random = SecureRandom.random_number * sum
12
- a.each_with_index do |(k,v),j|
16
+ a.each_with_index do |(k, v), j|
13
17
  if random <= v
14
18
  b << a.delete_at(j).first
15
19
  sum -= v
@@ -1,4 +1,4 @@
1
1
  module WeightedShuffle
2
2
  # weighted_shuffle version
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -3,33 +3,63 @@ require 'spec_helper'
3
3
  module WeightedShuffle
4
4
  describe Dealer do
5
5
  describe '#weighted_shuffle' do
6
- [[ [[:a, 1], [:b, 2]], [:a, :b] ],
7
- [ [[1, 1], [2, 3]], [1, 2] ],
8
- [ [[2.0, 1], [:a, 1.5], ['a', 2.3]], [2.0, :a, 'a'] ]]
9
- .each do |input, output|
10
-
11
- it "returns a permutation of #{output} given #{input}" do
12
- output.permutation.to_a.should include(
13
- Dealer.new(input).weighted_shuffle)
6
+
7
+ context 'without factor' do
8
+ [[ [[:a, 1], [:b, 2]], [:a, :b] ],
9
+ [ [[1, 1], [2, 3]], [1, 2] ],
10
+ [ [[1, 1], [2, 0]], [1, 2] ],
11
+ [ [[1, 1], [2, 0], [3, 0]], [1, 2, 3] ],
12
+ [ [[2.0, 1], [:a, 1.5], ['a', 2.3]], [2.0, :a, 'a'] ]]
13
+ .each do |input, output|
14
+
15
+ it "returns a permutation of #{output} given #{input}" do
16
+ expect(output.permutation.to_a).to include(
17
+ Dealer.new(input).weighted_shuffle)
18
+ end
19
+
20
+ it 'gives correct relative frequency for first position' do
21
+ weights = input.map(&:last)
22
+ # calculate probabiities
23
+ sum = weights.sum
24
+ probabilities = weights.map { |weight| weight.to_f / sum }
25
+
26
+ # establish frequencies
27
+ size = 1000
28
+ samples = size.times.map { Dealer.new(input).weighted_shuffle }
29
+ frequencies = input.map(&:first).map do |e|
30
+ samples.select { |s| s.first == e }.count.to_f / size
31
+ end
32
+
33
+ # calculate sum of absolute values of differences
34
+ diff = probabilities.zip(frequencies).map { |p,f| (p - f).abs }.sum
35
+ expect(diff).to be < 0.1
36
+ end
14
37
  end
38
+ end
15
39
 
16
- it 'gives correct relative frequency for first position' do
17
- weights = input.map(&:last)
18
- # calculate probabiities
19
- sum = weights.sum
20
- probabilities = weights.map { |e| e.to_f / sum }
40
+ context 'with factor' do
21
41
 
22
- # establish frequencies
42
+ it 'big factor has a big impact' do
43
+ input = [[1, 1], [2, 1], [3, 1.01]]
44
+ samples = 10_000.times.map do
45
+ Dealer.new(input).weighted_shuffle factor: 100_000
46
+ end
47
+ expect(samples.map(&:first).count(3)).to eq(samples.size)
48
+ end
49
+
50
+ it 'gives expected relative frequency ' do
51
+ input = [[:a, 1], [:b, 2]]
23
52
  size = 1000
24
- sample = size.times.map { Dealer.new(input).weighted_shuffle }
25
- frequencies = input.map(&:first)
26
- .map do |e|
27
- sample.select { |s| s.first == e }.count.to_f / size
28
- end
53
+ samples = size.times.map do
54
+ Dealer.new(input).weighted_shuffle factor: 2
55
+ end
56
+ frequencies = input.map(&:first).map do |e|
57
+ samples.select { |sample| sample.first == e }.count.to_f / size
58
+ end
29
59
 
30
- # calculate sum of absolute values of differences
31
- diff = probabilities.zip(frequencies).map { |p,f| (p - f).abs }.sum
32
- diff.should < 0.1
60
+ diff = [1.0 / 5, 4.0 / 5].zip(frequencies).map { |p, f| (p - f).abs }
61
+ .sum
62
+ expect(diff).to be < 0.1
33
63
  end
34
64
  end
35
65
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
7
7
  gem.version = WeightedShuffle::VERSION
8
8
  gem.summary = %q{Fisher-Yates-Shuffle algorithm with weight support}
9
9
  gem.description = <<-EOF
10
- An extension to the Fisher-Yates-Shuffle algorithm to support weights. It
10
+ An extension to the Fisher-Yates-Shuffle algorithm to support weights. It
11
11
  includes a ruby core extension for arrays.
12
12
  EOF
13
13
  gem.license = "MIT"
@@ -26,4 +26,9 @@ Gem::Specification.new do |gem|
26
26
  gem.add_development_dependency 'simplecov'
27
27
  gem.add_development_dependency 'coveralls'
28
28
  gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
29
+
30
+ if ENV['RUBY_VERSION'] =~ /rbx/
31
+ gem.add_dependency 'rubysl'
32
+ gem.add_development_dependency 'rubinius-coverage'
33
+ end
29
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weighted_shuffle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-09 00:00:00.000000000 Z
12
+ date: 2014-01-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -92,7 +92,7 @@ dependencies:
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0.2'
94
94
  description: ! " An extension to the Fisher-Yates-Shuffle algorithm to support weights.
95
- \ It\n includes a ruby core extension for arrays.\n"
95
+ It\n includes a ruby core extension for arrays.\n"
96
96
  email: frank.eckert@boost-project.com
97
97
  executables: []
98
98
  extensions: []
@@ -103,7 +103,6 @@ files:
103
103
  - .rspec
104
104
  - .travis.yml
105
105
  - Gemfile
106
- - Gemfile.lock
107
106
  - LICENSE.txt
108
107
  - README.md
109
108
  - Rakefile
@@ -136,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
135
  version: '0'
137
136
  requirements: []
138
137
  rubyforge_project:
139
- rubygems_version: 1.8.24
138
+ rubygems_version: 1.8.23
140
139
  signing_key:
141
140
  specification_version: 3
142
141
  summary: Fisher-Yates-Shuffle algorithm with weight support
@@ -1,49 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- weighted_shuffle (0.1.0)
5
- activesupport
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activesupport (3.2.13)
11
- i18n (= 0.6.1)
12
- multi_json (~> 1.0)
13
- colorize (0.5.8)
14
- coveralls (0.6.7)
15
- colorize
16
- multi_json (~> 1.3)
17
- rest-client
18
- simplecov (>= 0.7)
19
- thor
20
- diff-lcs (1.2.4)
21
- i18n (0.6.1)
22
- mime-types (1.23)
23
- multi_json (1.7.6)
24
- rest-client (1.6.7)
25
- mime-types (>= 1.16)
26
- rspec (2.13.0)
27
- rspec-core (~> 2.13.0)
28
- rspec-expectations (~> 2.13.0)
29
- rspec-mocks (~> 2.13.0)
30
- rspec-core (2.13.1)
31
- rspec-expectations (2.13.0)
32
- diff-lcs (>= 1.1.3, < 2.0)
33
- rspec-mocks (2.13.1)
34
- rubygems-tasks (0.2.4)
35
- simplecov (0.7.1)
36
- multi_json (~> 1.0)
37
- simplecov-html (~> 0.7.1)
38
- simplecov-html (0.7.1)
39
- thor (0.18.1)
40
-
41
- PLATFORMS
42
- ruby
43
-
44
- DEPENDENCIES
45
- coveralls
46
- rspec (~> 2.4)
47
- rubygems-tasks (~> 0.2)
48
- simplecov
49
- weighted_shuffle!