3

I want to get a random element from 0 to a huge number (2^31).

I tried creating an Array from such a Range (so I can use Swift's Array.randomElement), as done here:

let myArray: [Int64] = [Int64](0...4294967292)

Which compiles, but crashes with:

MyPoject(1569,0x100cc2f40) malloc: can't allocate region mach_vm_map(size=34359738368) failed (error code=3) MyProject(1569,0x100cc2f40) malloc: set a breakpoint in malloc_error_break to debug

Of course, I can write a custom function to create the array, but that smells, especially because the array will be the exact same every time.

Does Swift provide a better solution?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382

2 Answers2

6

The error message

malloc: can't allocate region mach_vm_map(size=34359738368)

tells that the runtime could not allocate 32GB memory – that is what an array of 4294967292 64-bit integers would require at

let myArray: [Int64] = [Int64](0...4294967292)

But there is no need to create an array for that purpose. As of Swift 4.2 you can simply call

let rnd = Int64.random(in: 0...4294967292)
// or
let rnd = Int64.random(in: 0..<4294967293)

using one of the

static func random(in range: ClosedRange<Self>) -> Self
static func random(in range: Range<Self>) -> Self

methods of the FixedWidthInteger protocol.

Finally note that 4294967292 is not 2^31 = 2147483648 – if the intention is to create a random number in the range from 0 (inclusive) to 2^31 (exclusive) then

let rnd = Int32.random(in: 0...Int32.max)

would to the trick.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Does it make sense to add the explanation of the memory error to the question, as perhaps one can have such a question even whilst knowing the meaning of that error? –  Dec 09 '18 at 11:30
  • @DaniSpringer: It is not entirely clear to me what you are suggesting, but explanations should be in answers, not in questions. However, you could change the title to *“malloc error when generating huge random number”* to distinguish it from other questions about random numbers, and to make it easier to find for others with a similar problem. – Martin R Dec 09 '18 at 11:35
  • If you want non-repeating random values from 0 to 2^31 its a more complex problem. My usual approach of building an array of all possible values and removing one random value at a time until you have the desired number of values won't work because it requires too much memory, as the OP discovered. You'd have to add random values to a set until you had the desired number of entries, then convert the set into an array. – Duncan C Dec 09 '18 at 12:51
3

ClosedRange also has a randomElement method in Swift 4.2:

print((0...4294967292).randomElement()!)

Note that you said you want a random number between 0 and 2^31, but you used 1...4294967292 instead of 0...4294967292 in your example.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Ah it does, that's good to know. And wow, **SO** you did it in under 20 mins Yeah the truth is 0 and 1 are both good (which is why I didn't realize I was inconsistent), because my idea was I can add 1 to the result later. –  Dec 09 '18 at 11:22