- Notifications
You must be signed in to change notification settings - Fork91
Random data generation in Swift
License
nvzqz/RandomKit
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
RandomKit is a Swift framework that makes random data generation simple and easy.
- Build Status
- Installation
- Benchmark
- Usage
- Extra
- License
Branch | Status |
---|---|
master |
- Platforms:
- macOS 10.9+
- iOS 8.0+
- watchOS 2.0+
- tvOS 9.0+
- Linux
- Xcode 8.0+
- Swift 3.0.2+ & 4.0
RandomKit is possibly also compatible with FreeBSD, Android, and Windows(under Cygwin) but has not been tested for those platforms.
TheSwift Package Manager is adecentralized dependency manager for Swift.
Add the project to your
Package.swift
.import PackageDescriptionletpackage=Package( name:"MyAwesomeProject", dependencies:[.Package(url:"https://github.com/nvzqz/RandomKit.git", majorVersion:5)])
Import the RandomKit module.
import RandomKit
CocoaPods is a centralized dependency manager forObjective-C and Swift. Gohereto learn more.
Add the project to yourPodfile.
use_frameworks!pod'RandomKit','~> 5.2.3'
If you want to be on the bleeding edge, replace the last line with:
pod'RandomKit',:git=>'https://github.com/nvzqz/RandomKit.git'
Run
pod install
and open the.xcworkspace
file to launch Xcode.Import the RandomKit framework.
import RandomKit
Carthage is a decentralized dependencymanager for Objective-C and Swift.
Add the project to yourCartfile.
github "nvzqz/RandomKit"
Run
carthage update
and followthe additional stepsin order to add RandomKit to your project.Import the RandomKit framework.
import RandomKit
Various components of RandomKit can be easily benchmarked by runningbenchmark.sh
.
./benchmark.sh [FLAGS] [PROTOCOLS]
Use the--help
flag for information regarding how to use it.
Note: The default count is 10000000, which is A LOT if using the--array
flag.This can be changed by passing an argument into--count
or-c
.
Try it out for yourself! Download the repo and open 'RandomKit.playground'.
TheRandomGenerator
protocol defines basic methods for generating primitivevalues and randomizing a buffer.
All provided types that conform toRandomGenerator
have a staticdefault
value that can be passed as aninout
argument to generation functions.
letvalue=Int.random(using:&Xoroshiro.default)
ARC4Random
- Because the symbols for the
arc4random
family of functions aren'texported with Foundation on Linux and other platforms, they're dynamicallyloaded at runtime.
- Because the symbols for the
DeviceRandom
- Reads from "/dev/random" or "/dev/urandom" as its source.
MersenneTwister
Xoroshiro
Xorshift
XorshiftStar
ChaCha
SeedableRandomGenerator
is for types that can be seeded with some associatedSeed
type.
TheRandomBytesGenerator
protocol is for types that specialize in generating aspecific type that fills up a number of bytes. For example,MersenneTwister
specializes in generatingUInt64
whileXorshift
generatesUInt32
values.
For single-threaded programs, it is safe to use a global generator instance suchasXoroshiro.default
as a source of randomness.
For multi-threaded programs, the thread-local instances should be used. Thisallows for different threads to use their own separate random generators withouta shared mutable state.
In the following example,randomGenerator
is unique to each thread.
letrandomBytes=Xoroshiro.withThreadLocal{ randomGeneratorinreturn[UInt8](randomCount:1000, using:&randomGenerator)}
Thread-local generators are deallocated upon thread exit, so there's no need toworry about cleanup.
It's recommended to not callwithThreadLocal(_:)
or get thethreadLocal
pointer each individual time it's needed. Retrieving the thread-local instanceincurs avoidable overhead.
// Badletvalue=Int.random(using:&Xoroshiro.threadLocal.pointee)array.shuffle(using:&Xoroshiro.threadLocal.pointee)// GoodletthreadLocal=Xoroshiro.threadLocalletvalue=Int.random(using:&threadLocal.pointee)array.shuffle(using:&threadLocal.pointee)// BetterXoroshiro.withThreadLocal{ randomGeneratorinletvalue=Int.random(using:&randomGenerator) array.shuffle(using:&randomGenerator)}
As a shortcut, you can even apply a function directly as a parameter.
letvalue=Xoroshiro.withThreadLocal(Int.random)
Prior tov4.4.0,thread safety could be achieved by instantiating a new seeded instance of agivenRandomGenerator
type. The problem with this is that unnecessary seedingoccurs each time. With this, the generator is seeded once and can then be reusedat later points.
Shortcuts to the reseeding version of a generator are also available:
Xoroshiro.withThreadLocalReseeding{...}
Which isway better than writing:
ReseedingRandomGenerator.withThreadLocal(createdWith:{Xoroshiro.reseeding}){...}
RandomKit is very protocol-oriented, which gives it the ability to be veryflexible and modular.
A protocol for types that can generate random values using aRandomGenerator
.
A protocol for types that can generate optional random values within a rangeusing aRandomGenerator
.
Int.random(in:0..<0, using:&randomGenerator) // nil
A protocol for types that can generate random values within a closed rangeusing aRandomGenerator
.
Int.random(in:-100...100, using:&randomGenerator) // -79
A protocol for types that can generate random values from a base value toanother value, noninclusive.
The base value for integers is 0. This means that callingrandom(to:using:)
ona negative value will yield a random negative value or zero whereas a positivevalue will yield a random positive value or zero.
Ifvalue
==randomBase
,value
will be returned forrandom(to:using:)
.
Int.random(to:2, using:&randomGenerator) // Either 0 or 1Int.random(to:0, using:&randomGenerator) // Always 0Int.random(to:32, using:&randomGenerator) // 15Int.random(to:-5, using:&randomGenerator) // -3
A protocol for types that can generate random values from a base value throughanother value, inclusive.
The same rules regarding the base value ofRandomToValue
apply toRandomThroughValue
.
A protocol for types whose instances can have random elements retrieved.
["Bob","Cindy","May","Charles","Javier"].random(using:&randomGenerator) // "Charles""Hello".characters.random(using:&randomGenerator) // "e"
Some Foundation types likeNSArray
conform to this protocol.
A protocol for types whose instances can have random elements retrieved fromwithin aRange<Index>
.
[20,37,42].random(in:1..<3, using:&randomGenerator) // Either 37 or 42
A protocol for types whose elements can be shuffled.
// Array[1,2,3,4,5].shuffled(using:&randomGenerator) // [3, 4, 1, 5, 2]// Dictionary["a":1,"b":2,"c":3].shuffled(using:&randomGenerator) // ["a": 3, "b": 1, "c": 2]
The mutable counterpart ofshuffled(using:)
isshuffle(using:)
.
For betterArray
shuffling performance, consider shuffling in-place withshuffle(using:)
.
Similar toShuffleable
, except no element is ever in its initial position.
All of Swift's native integer types conform to theRandom-
protocols.
Therandom(using:)
function creates an integer of any value. As a result,negative values can result for signed integers.
Int.random(using:&randomGenerator) // An Int within Int.min and Int.maxInt.random(in:10...20, using:&randomGenerator) // An Int within 10 and 20
To create a positive signed integer, userandom(to:using:)
orrandom(through:using:)
.
Int.random(to:1000, using:&randomGenerator) // 731Int.random(through:10, using:&randomGenerator) // 4
Signed integers can be created from any range, without danger of overflow.
Int.random(in:(.min+1000)...(.max-200), using:&randomGenerator) // 5698527899712144154
Generate a random floating point value from within a range or0.0...1.0
bydefault.
Double.random(using:&randomGenerator) // 0.9813615573117475Double.random(in:-10...10, using:&randomGenerator) // -4.03042337718197Float.random(in:-10...10, using:&randomGenerator) // 5.167088Float80.random(in:-10...10, using:&randomGenerator) // -3.63204542399198874
AllFloatingPoint
types can also conform toRandomInClosedRange
out-of-the-box.
Bool.random(using:)
has a 50/50 chance of beingtrue
.
If you need different probability, there's alsorandom(withWeight:using:)
,which has 1 inweight
chance of beingtrue
.
String
,Character
, andUnicodeScalar
generate values within" "..."~"
bydefault.
String.random(ofLength:10, using:&randomGenerator) // "}+[=Ng>$w1"String.random(ofLength:10, in:"A"..."z", using:&randomGenerator) // "poUtXJIbv["Character.random(using:&randomGenerator) // "#"Character.random(in:"A"..."z", using:&randomGenerator) // "s"
An array of random values can be generated for types conforming toRandom
withinit(randomCount:using:)
.
Similar initializers exist for all otherRandom-
protocols.
letrandoms=Array<Int>(randomCount:100, using:&randomGenerator) // [8845477344689834233, -957454203475087100, ...]
For types conforming toUnsafeRandom
, a faster alternative isinit(unsafeRandomCount:using:)
.This initializer fills the buffer directly rather than usingrandom(using:)
.
letunsafeRandoms=Array<Int>(unsafeRandomCount:100, using:&randomGenerator) // [759709806207883991, 4618491969012429761, ...]
A benchmark of generating 1000 randomInt
arrays of 10000 count:
Generator | Time (in seconds) |
---|---|
Xoroshiro | 0.0271 |
Xorshift | 0.0568 |
XorshiftStar | 0.0319 |
ChaCha | 0.2027 |
MersenneTwister | 0.0432 |
ARC4Random | 0.2416 |
DeviceRandom | 5.3348 |
Note: Results may vary due to various factors.
This same benchmark can be run with:
./benchmark.sh --all-generators --array 10000 --count 1000
A randomDate
can be generated between twoDate
orTimeInterval
values.
The defaultrandom(using:)
function returns aDate
withinDate.distantPast
andDate.distantFuture
.
Date.random(using:&randomGenerator) // "Aug 28, 2006, 3:38 AM"Date.random(in:Date.distantPast...Date(), using:&randomGenerator) // "Feb 7, 472, 5:40 AM"
TheDecimal
type conforms to variousRandom-
protocols.
Therandom(using:)
function returns aDecimal
between 0 and 1 by default.
Decimal.random(using:&randomGenerator) // 0.87490000409886706715888973957833129437Decimal.random(in:0.0...10.0, using:&randomGenerator) // 6.5464639772070720738747790627821299859
A random number can be generated from within an integer or double range, or0...100
by default.
NSNumber.random(using:&randomGenerator) // 79NSNumber.random(in:-50...100, using:&randomGenerator) // -27NSNumber.random(in:100...200, using:&randomGenerator) // 149.6156950363926
A random color can be generated, with or without random alpha.
NSColor.random(using:&randomGenerator) // r 0.694 g 0.506 b 0.309 a 1.0NSColor.random(alpha:true, using:&randomGenerator) // r 0.859 g 0.57 b 0.409 a 0.047UIColor.random(using:&randomGenerator) // r 0.488 g 0.805 b 0.679 a 1.0UIColor.random(alpha:true, using:&randomGenerator) // r 0.444 g 0.121 b 0.602 a 0.085
BecauseCGFloat
conforms toFloatingPoint
, it conforms toRandomInClosedRange
just like howDouble
andFloat
do.
CGFloat.random(using:&randomGenerator) // 0.699803650379181CGFloat.random(in:0...100, using:&randomGenerator) // 43.27969591675319
A random point can be generated from within ranges for x and y.
CGPoint.random(using:&randomGenerator) // {x 70.093 y 95.721}CGPoint.random(xRange:0...200, yRange:0...10, using:&randomGenerator) // {x 73.795 y 0.991}
A random size can be generated from within ranges for width and height.
CGSize.random(using:&randomGenerator) // {w 3.744 h 35.932}CGSize.random(widthRange:0...50, heightRange:0...400, using:&randomGenerator) // {w 38.271 h 239.636}
A random rectangle can be generated from within ranges for x, y, width, andheight.
CGRect.random(using:&randomGenerator) // {x 3.872 y 46.15 w 8.852 h 20.201}CGRect.random(xRange:0...50, yRange:0...100, widthRange:0...25, heightRange:0...10, using:&randomGenerator) // {x 13.212 y 79.147 w 20.656 h 5.663}
A random vector can be generated from within ranges for dx and dy.
CGVector.random(using:&randomGenerator) // {dx 13.992 dy 89.376}CGVector.random(dxRange:0...50, dyRange:0...10, using:&randomGenerator) // {dx 35.224 dy 13.463}
RandomKit extensions for Károly'sBigInt library are available inRandomKitBigInt.
RandomKit and its assets are released under theMIT License. Assetscan be found in theassets
branch.
Parts of this project utilize code written byMatt Gallagher and, in conjunctionwith the MIT License, are licensed with that foundhere.
About
Random data generation in Swift