Skip to content

Commit fce5982

Browse files
committedMay 2, 2015
Improved Array#sample.
1 parent 7f35e45 commit fce5982

File tree

1 file changed

+82
-17
lines changed

1 file changed

+82
-17
lines changed
 

Diff for: ‎kernel/common/array.rb

+82-17
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,20 @@ def rotate!(cnt=1)
13051305
replace ary
13061306
end
13071307

1308+
class SampleRandom
1309+
def initialize(rng)
1310+
@rng = rng
1311+
end
1312+
1313+
def rand(size)
1314+
random = Rubinius::Type.coerce_to_collection_index @rng.rand(size)
1315+
raise RangeError, "random value must be >= 0" if random < 0
1316+
raise RangeError, "random value must be less than Array size" unless random < size
1317+
1318+
random
1319+
end
1320+
end
1321+
13081322
def sample(count=undefined, options=undefined)
13091323
return at Kernel.rand(size) if undefined.equal? count
13101324

@@ -1326,29 +1340,80 @@ def sample(count=undefined, options=undefined)
13261340
end
13271341

13281342
rng = options[:random] if options
1329-
rng = Kernel unless rng and rng.respond_to? :rand
1330-
1331-
unless count
1332-
random = Rubinius::Type.coerce_to_collection_index rng.rand(size)
1333-
raise RangeError, "random value must be >= 0" if random < 0
1334-
raise RangeError, "random value must be less than Array size" unless random < size
1335-
1336-
return at random
1343+
if rng and rng.respond_to? :rand
1344+
rng = SampleRandom.new rng
1345+
else
1346+
rng = Kernel
13371347
end
13381348

1349+
return at rng.rand(size) unless count
1350+
13391351
count = size if count > size
1340-
result = Array.new self
1341-
tuple = Rubinius::Mirror::Array.reflect(result).tuple
13421352

1343-
count.times do |i|
1344-
random = Rubinius::Type.coerce_to_collection_index rng.rand(size)
1345-
raise RangeError, "random value must be >= 0" if random < 0
1346-
raise RangeError, "random value must be less than Array size" unless random < size
1353+
case count
1354+
when 0
1355+
return []
1356+
when 1
1357+
return [at(rng.rand(size))]
1358+
when 2
1359+
i = rng.rand(size)
1360+
j = rng.rand(size)
1361+
if i == j
1362+
j = i == 0 ? i + 1 : i - 1
1363+
end
1364+
return [at(i), at(j)]
1365+
else
1366+
if size / count > 3
1367+
abandon = false
1368+
spin = 0
13471369

1348-
tuple.swap i, random
1349-
end
1370+
result = Array.new count
1371+
i = 1
1372+
1373+
result[0] = rng.rand(size)
1374+
while i < count
1375+
k = rng.rand(size)
1376+
j = 0
1377+
1378+
while j < i
1379+
while k == result[j]
1380+
spin += 1
1381+
if spin > 100
1382+
abandon = true
1383+
break
1384+
end
1385+
k = rng.rand(size)
1386+
end
1387+
break if abandon
1388+
1389+
j += 1
1390+
end
1391+
1392+
break if abandon
1393+
1394+
result[i] = k
1395+
1396+
i += 1
1397+
end
1398+
1399+
unless abandon
1400+
i = 0
1401+
while i < count
1402+
result[i] = at result[i]
1403+
i += 1
1404+
end
1405+
1406+
return result
1407+
end
1408+
end
1409+
1410+
result = Array.new self
1411+
tuple = Rubinius::Mirror::Array.reflect(result).tuple
13501412

1351-
return count == size ? result : result[0, count]
1413+
count.times { |i| tuple.swap i, rng.rand(size) }
1414+
1415+
return count == size ? result : result[0, count]
1416+
end
13521417
end
13531418

13541419
def select!(&block)

0 commit comments

Comments
 (0)