Adding non-repeating random numbers to an array [duplicate] - arrays

This question already has answers here:
How do I shuffle an array in Swift?
(25 answers)
Closed 5 years ago.
Hi I am trying to create an array from non-repeating random numbers.
I wrote below code numberOfAnimals which is 10 currently shows how many numbers will be in the array. When I run this in playground I get
"[6, 5, 1, 4, 7, 0]\n" as output of print statement so in total 6 numbers instead of 10. To avoid it I am decreasing value of i in if statement in case random number exists in the array already, in this case for loop needs an additional loop to reach 10 but still it does not work.
Can you please check where is the mistake or give me another code suggestion that can work ?
import UIKit
var array = [Int]()
var max : Int = 10
var numberOfAnimals : Int = 10
for var i in 0..<numberOfAnimals {
let randomNumber : Int = Int(arc4random_uniform(UInt32(max)))
if array.contains(randomNumber) {
i = i - 1
} else {
array.append(randomNumber)
}
}
print(array)

Why not have a while loop that continues until you have ten numbers in your array.
while array.count < 10 {
...
if !array.contains(randomNumber) {
array.append(randomNumber)
}
}

If you repeat your loop until every number has been hit, the worst case runtime of your algorithm is infinite. As far is I understand your problem, you have an array with fixed size and fixed elements, which you want to be shuffled, so why not do this? I found a nice algorithm in another thread:
extension MutableCollection {
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
let numberOfAnimals = [1,2,3,4,5,6,7,8,9,10]
array.shuffle()
print(array)

I'd suggest a slight modification of #Joakim Danielson 's answer if you can afford the extra space and implement a set - if an array isn't of vital importance you may want to consider a set for your implementation as well, just $0.02
var array = [Int]()
var seen = Set<Int>()
let max = 10
while array.count < max {
let number = Int(arc4random_uniform(UInt32(max)))
if seen.contains(number) {
continue
}
array.append(number)
seen.insert(number)
}
print(array)

Related

Get a set number of random items from array [duplicate]

This question already has answers here:
Get random elements from array in Swift
(6 answers)
Closed 5 years ago.
I want to write an extension which allows me to put in an array and return a set number of elements from that array with no repeated items. How would I do that? This is what I have so far but it is not perfect. It does not consider duplicates and it does not seem like the best way for this to be done. I was thinking it might make sense to use a set for duplicates.
extension Array {
func randomElement(numberOfItems:Int) -> [Element] {
var finalReturn = Array()
for i in 0..<numberOfItems {
finalReturn.append(self[Int(arc4random_uniform(UInt32(self.count)))])
}
return finalReturn
}
}
usage should be like this.
let selected = allData.randomElement(numberOfItems: 10)
Here is one way to do it:
extension Array {
func randomElements(number: Int) -> [Element] {
guard number > 0 else { return [Element]() }
var remaining = self
var chosen = [Element]()
for _ in 0 ..< number {
guard !remaining.isEmpty else { break }
let randomIndex = Int(arc4random_uniform(UInt32(remaining.count)))
chosen.append(remaining[randomIndex])
remaining.remove(at: randomIndex)
}
return chosen
}
}
Sample:
let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let randomThree = testArray.randomElements(number: 3)
// randomThree is [1, 5, 4]
Depending on your use case, you may want to change the behavior when the number of elements requested is greater than the number of elements in the array.
In my sample above, if this is the case, I return the maximum number of elements possible (the number of elements in the original array). Alternatively, you could give an error or return nil.

Changing the size of an array inside of in for-in loop

I was presented this problem where I need to find matching numbers in a given array (socks) and print out how many socks found inside that array. Here is my code:
let numberOfSocks = 9
let socksArray = [10, 20, 20, 10, 10, 30, 50, 10]
func findSocks(numberOfSocks: Int, array: [Int]) {
var arr = array
var uniqueSocks = Array(Set(array))
var matchedPairs = 0
var sockCounter = 0
for i in 0..<uniqueSocks.count { // After the search, remove the element at index
sockCounter = 0
for j in 0..<arr.count {
if uniqueSocks[i] == arr[j] {
sockCounter += 1
if sockCounter % 2 == 0 {
matchedPairs += 1
sockCounter = 0
}
}
}
}
print(matchedPairs)
}
findSocks(numberOfSocks: numberOfSocks, array: socksArray)
Firstly, I have removed all the duplicates in the array so it gives me a list unique socks that I need to search for. However, I wanted to optimize this algorithm by remove the sock that I have already searched for, I have tried arr.remove(at:) but It gave me an out of bound, I have a feeling that arr.count is not updated correctly. Any help is welcome, thanks!
I think that you're overthinking the problem, focusing on small details rather than the big picture. What you want to end up with is essentially a dictionary where the keys are the unique values in the array, and the values are the number of times those values appear in the array. So start with your dictionary:
var counts = [Int : Int]()
There's no need for your arr and numberOfSocks variables. Instead of the latter, just use socksArray.count, which clearly will always be in sync with the true size of the array.
Now loop through your socks. For each sock value, increment its count in the counts dictionary or, if it's not already in the dictionary, add it and give it a count of 1.
for sock in socks {
if !counts.contains(sock) {
counts[sock] = 1
} else {
counts[sock] = counts[sock] + 1
}
}
There are more concise ways to do this, but I think that this one is the easiest to read.

Swift - speed and efficiency of higher order functions (reduce)

Quick question please about the efficiency of higher order swift functions with large input data. During a recent test I had a question about finding 'equlibirum indexes' in arrays- i.e. the index of an array where the sum of all elements below the index equals the sum of all elements above the index
An equilibrium index of this array is any integer P such that 0 ≤ P <
N and the sum of elements of lower indices is equal to the sum of
elements of higher indices, i.e.
A[0] + A[1] + ... + A[P−1] = A[P+1] + ... + A[N−2] + A[N−1].
The challenge was to write a short function which computed the first (or any) index which was considered 'equilibirum'.
I put together a simple snippet which scored highly but failed some of the 'performance' tests which used large input data (array sizes around 100,000).
Here's the code
public func solution(inout A : [Int]) -> Int {
var index = 0;
for _ in A {
let sumBefore = A[0...index].reduce(0) { $0 + $1 }
let sumAfter = A[index...A.count-1].reduce(0) { $0 + $1 }
if (sumBefore == sumAfter) { return index; }
index += 1;
}
return -1;
}
Would anyone please be able to explain why the code performs so poorly with large sets of data, or any recommended alternatives?
Here, for example is a description of a failing perfomance test:
Large performance test, O(n^2) solutions should fail.
✘ TIMEOUT ERROR
running time: >6.00 sec., time limit: 0.22 sec.
It looks like the challenge is failing because your solution is O(n^2).
Your for loop, along with 2 sequential reduces inside, make your solution ~ O(2*n^2) since reduce goes through all the elements again.
A simpler solution is to first compute the whole sum, and then iterate through the elements once, subtracting each value from the whole sum, one by one, thus having access to the left and right sums, for comparison.
Using Swift 3.0, Xcode 8:
func findEquilibriumIndex(in array: [Int]) -> Int? {
var leftSum = 0
var rightSum = array.reduce(0, combine: +)
for (index, value) in array.enumerated() {
rightSum -= value
if leftSum == rightSum {
return index
}
leftSum += value
}
return nil
}
let sampleArray = [-7, 1, 5, 2, -4, 3, 0]
findEquilibriumIndex(in: sampleArray)
The problem is not that "the built-in functions perform so poorly."
Your solution is slow because in each iteration, N elements are
added (N being the length of the array). It would be more efficient
to compute the total sum once and update the "before sum"
and "after sum" while traversing through the array. This reduces
the complexity from O(N^2) to O(N):
public func solution(A : [Int]) -> Int {
var sumBefore = 0
var sumAfter = A.reduce(0, combine: +)
for (idx, elem) in A.enumerate() {
sumAfter -= elem
if sumBefore == sumAfter {
return idx
}
sumBefore += elem
}
return -1
}
(Swift 2.2, Xcode 7.3.1)
Remarks:
There is no reason to pass the array as inout parameter.
An operator (in this case +) can be passed as a argument to the reduce() function.
enumerate() returns a sequence of array indices together with
the corresponding element, this saves another array access.
Note also that a more "Swifty" design would be to make the return type
an optional Int? which is nil if no solution was found.
The incrementalSums extension
If you define this extension
extension Array where Element : SignedInteger {
var incrementalSums: [Element] {
return Array(reduce([0]) { $0.0 + [$0.0.last! + $0.1] }.dropLast())
}
}
given an array of Int(s) you can build an array where the Int at the n-th position represents the sum of the values from 0 to (n-1) in the original array.
Example
[1, 2, 3, 10, 2].incrementalSums // [0, 1, 3, 6, 16]
The equilibriumIndex function
Now you can build a function like this
func equilibriumIndex(nums: [Int]) -> Int? {
let leftSums = nums.incrementalSums
let rightSums = nums.reversed().incrementalSums.reversed()
return Array(zip(leftSums, rightSums)).index { $0 == $1 }
}
Here is a functional version of the solution in Swift 3
let total = sampleArray.reduce(0,+)
var sum = 0
let index = sampleArray.index{ v in defer {sum += v}; return sum * 2 == total - v }
If I understand correctly the element at the resulting index is excluded from the sum on each side (which I'm not certain the other solutions achieve)

Random array range 1-4 with each number only printed twice

I'm trying to learn how to assign a range of numbers 1 to 4 to an array with each number printed twice. I can't figure out how to use the random range to print numbers specific amounts of times.
Not exactly sure if this is even right. I've haven't really working with for loops, but i did learn them. Not even complete because of the roadblock of how to do this.
By the way, also might help to say this is a card matching game I'm making, so thats why i only need to print twice.
/*for index in imageArray
{
imageArray[index] =
}*/
To assign numbers one through four to an array:
let numbers = Array(1...4)
To assign the numbers one through four twice to an array:
let numbers = Array(1...4) + Array(1...4)
To shuffle those numbers:
var shuffledNumbers = numbers.shuffled()
In Swift 4.2 and later, you can use the built-in shuffled method. In earlier versions, you'd have to write your own, e.g. using the Fisher-Yates algorithm:
// see http://stackoverflow.com/a/24029847/1271826
extension MutableCollection {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
if count < 2 { return } // empty and single-element collections don't shuffle
for i in 0 ..< count - 1 {
let j = Int(arc4random_uniform(UInt32(count - i)))
if j != 0 {
let current = index(startIndex, offsetBy: i)
let swapped = index(current, offsetBy: j)
swapAt(current, swapped)
}
}
}
/// Return shuffled collection the elements of `self`.
func shuffled() -> Self {
var results = self
results.shuffle()
return results
}
}

(Swift) Trying to iterate through remaining elements in array, add those elements independently, and check to see if they match random number given

Disclaimer: new to swift.
I have a simple dice game that needs to match the number shown on dice to the tiles(ints) remaining on the board. I am able to enumerate through the array for a direct match but, if the dice show a greater number than the tiles individually I need to check if those tiles(ints), in any combination, can also match the number on dice shown.
for loops, do-while, enumerations.....head is starting to explode. Example below shows a condensed version of where i think i'm going. any help would be great.
var array = [1,2,3,4]
func roundOver () {
var ran = Int(arc4random_uniform(7) % 7 + 1)
for (index,value)in enumerate(array) {
if value == ran {
println("match")
} else if value != ran {
do {..........huh?
If I understand your question correctly, you need to solve the "Subset sum problem":
Given a set S and a number x, is there a subset of S whose sum is equal to x?
This problem can be efficiently solved with "dynamic programming", as described in
the Wikipedia article. For small sets, a brute-force algorithm can be used which
simply tries all possible subsets. This can be recursively written as
func isSummableTo(array: [UInt], _ value: UInt) -> Bool {
if value == 0 {
// We have reached the exact sum
return true
} else if array.count == 0 {
// No elements left to try
return false
}
// Split into first element and remaining array:
var array1 = array
let first = array1.removeAtIndex(0)
// Try to build the sum without or with the first element:
return isSummableTo(array1, value) || (value >= first && isSummableTo(array1, value - first))
}
(Here I have assumed that you work only with non-negative integers.)
For example
isSummableTo([1, 3, 5, 10], 6) == true
because 1 + 5 = 6, and
isSummableTo([1, 3, 5, 10], 7) == false
because there is not subset of the numbers 1, 3, 5, 10 that sums up to 7.

Resources