قالب وردپرس درنا توس
Home / IOS Development / Random numbers in Swift – Ole Begemann

Random numbers in Swift – Ole Begemann



Working with random numbers in Swift used to be a bit of pain because there was no native random number API. Developers had to fall back on C APIs provided by the operating system, which are often easy to misuse. Furthermore, the preferred random number API is different between platforms (eg arc4random versus edge versus random ), making it difficult to write cross-platform code. 19659002] Swift 4.2 makes this much easier by including a native and fairly complete random number API in the default library. You can read about the entire API and its justification in Swift Evolution proposal SE-0202.)

Let's look at some usage examples. All number types have a static random (i :) method that takes a area (or ClosedRange ) and returns a random number in the specified area uniform distribution):

  Int .   Random     in :    1
... 1000 ) // 691 Double . ] : 0 . [ 1 ] : : 0xD800 ... 0xDFFF ) // → 55666

Modulo bias

Note that there is no random () method as do not take an area argument. This was a deliberate decision to protect users against a common fault called modulo bias. If there was such an API, you may be tempted to use the modulo division to generate a random number in a given area. For example:

  // Error! ❌ 
  la    between0And5    =    UInt8 .   random   ()  %    6 

This would actually produce a number between 0 and 5 (inclusive), but the random results are no longer evenly distributed! Swift API brings you nicely to the right solution:

  // Correct 
  la    between0And5    =    UInt8 .   Random     :    0 . <  6 )    // → 5 

If you need a random number all over the range of a type, passport

        in       in   [19659000].   min    ...  .   max )    // → 163 

Bool.random () is also one thing. Here is a small function that performs a number of coins and returns the number of heads and tails:

  func    coinToss   (  count    tossCount :    Int  ])    ->    [[19659000]] :    Int     Haler :    Int )    {
      var    ] [19659000]] [19659000]] [19659000]]    :    0     Tails :    0 ) 
      to [19659115] 19659009]    0 . <  tossCount    {
          la    isHeads    =    Boolean .   random [19659007] () 
          whose    isHeads    {
              tally .   head    + =    1 
        }    otherwise    {[19659131] tally .    tails    + =    1 
        } 
    } 
      return    tally 
} 

  la    ([19659008] heads   ] ] ] ]   [→19659007] (  counter :  ] 
  // → (Heads 54, Tails 46) 19659163] Collections have a  randomElement ()  method that returns a random item from the collection. The return type is a  Optional  because the collection may be empty (similar to  min  and  max  max  ] 

  la    emptyRange    =    10 . <  10 
  emptyRange .   isEmpty    // → true 
       randomElement      // → nil 

Here is an example that picks a random character from a string:

  la    feelings    =    "😀😂😊😍🤪😎😩😭😡" 
 ]    randomEmotion    =    emotions .   randomElement   () !    // → "🤪" 

Use mixed)

  

  

        ()    (  1 . ..   10   // [10, 3, 8, 1, 4, 6, 2, 7, 9, 5] 

There is also a mutant variant called shuffle () for shuffling a collection in place. It is defined for types that match both MutableCollection and RandomAccessCollection :

  var    number    =    Array   (  1 ]  10  ] 
  number .   shuffle   () 
  // → numbers are now [3, 10, 7, 4, 6, 9, 2, 5, 8, 1]

Standard RNG

All examples above implicitly use the standard random number generator (RNG) provided with the default library, called SystemRandomNumberGenerator .

SE-0202 does not prescribe a particular RNG, but it shows some desired properties of a good standard RNG:

A The inspiration is that this RNG should be cryptographic safe, provide reasonable performance, and should be sure of wire. If a supplier fails to provide these goals, they should document it clearly. ... if an RNG on a platform has the opportunity to fail, it must fail [i.e. trap] when it is unable to complete its operation.

Capture in the (extremely rare, if possible) error error was preferred over an API where each function that generated a random value had to cast or return a Optional . Such an API would either be extremely inconvenient to use or just cause users to cast their code with force unwraps without gain.

The default default RNG application algorithm can vary between platforms and Swift releases. The documentation provides the following guarantees about SystemRandomNumberGenerator :

SystemRandomNumberGenerator automatically sows, can be used in multiple threads, and uses a cryptographic secure algorithm whenever possible.

Custom RNGs

The purpose is that default RNG should be the right choice for most simple usage cases. However, if your code has special requirements for a random number generator, such as a particular algorithm or the ability to initialize RNG with a repeatable seed, you can implement your own RNG by adopting the RandomNumberGenerator protocol. The protocol has a single requirement: one next () method that gives 8 fresh bytes coincidence:

  public    protocol    RandomNumberGenerator    {
      /// Returns a 
      ] muting    func    next   ()    ->    UInt64 
} 

Notice that the protocol requires a steady distribution. The idea is that users who need random values ​​with uneven distributions can use the desired distribution to the sequence of evenly distributed coincidence in a second step.

Using a Custom RNG

all default library APIs to produce random values ​​provide an overload that allows users to pass in a custom random number generator. For example, the Int type has these two methods:

  Extension    Int    {
      Static    Func    Random   (  in [19659041] ] :            Int > )    ->    Int    {   ...  } [19659206] Static    FUNC    Random  <  T >   (  in    Variant :    Area  <[19659256] Int >  
                 : :    in    T )    ->    Int 
          where    T :    RandomNumberGenerator    19659079] {  ]  } 
      // The overloads that take a ClosedRange are not shown 
} 

The generator parameter is always passed inout because it is common for RNGs to mutate their condition when they generate new coincidence.

Here's how to use a custom RNG You must create a customizable variable to satisfy inout the requirement:

  var    mersenneTwister    =    MersenneTwisterRNG   (  ... ) [19659014] // Assume this exists 
  Int .   Random     in :    10 . <  20  [19659041] Using :    and   mersenneTwister ) 

If it makes sense for your own data types to produce random values, try following the default library pattern:

  • Provide a static random () -> Self method that always uses standard RNG. This may take additional parameters if it makes sense to limit random values ​​to a particular area.
  • Provide a second method, randomly (using generator: inout T) -> Even a generic random number generator.

Example: A one-man representing playing card suits. We use the compiler-synthesized allCases property (new in Swift 4.2) for implementation:

  enum    Suit :    String    ] CaseIterable    ] 
the case diamonds = "♦"
case clubs = "♣"
case hearts = "♥"
scissors spader = "♠"

static FUNC random () -> Suit {
var rng = SystemRandomNumberGenerator ()
return Suit . random : : & rng )
}

static FUNC random < ] : RandomNumberGenerator >
( with generator : inout T )


Source link