Speeding up Swift CodeFight Challenge - swift

Per Codefighters:
Note: Write a solution with O(n) time complexity and O(1) additional space complexity, since this is what you would be asked to do during a real interview.
Given an array a that contains only numbers in the range from 1 to a.length, find the first duplicate number for which the second occurrence has the minimal index. In other words, if there are more than 1 duplicated numbers, return the number for which the second occurrence has a smaller index than the second occurrence of the other number does. If there are no such elements, return -1.
Example
For a = [2, 3, 3, 1, 5, 2], the output should be firstDuplicate(a) = 3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3 has a smaller index than than second occurrence of 2 does, so the answer is 3.
For a = [2, 4, 3, 5, 1], the output should be firstDuplicate(a) = -1.
So here is what I came up with. It works but fails on the final test because it ran over 4000ms. I am stuck to what else I can do. Any Ideas to improve speed?
func firstDuplicate(a : [Int]) -> Int {
var duplicateIndexArray = [Int]()
for firstIndex in 0..<a.count {
for secondIndex in 0..<a.count {
if a[firstIndex] == a[secondIndex] && firstIndex != secondIndex {
print(firstIndex, secondIndex)
if !(duplicateIndexArray.contains(firstIndex)){
duplicateIndexArray.append(secondIndex)
break
}
}
}
}
// Check for duplicacy
if duplicateIndexArray.count > 0 {
print(duplicateIndexArray)
return a[duplicateIndexArray.min()!]
}
return -1
}

The O(n) time part is easy, but the O(1) additional space is a bit tricky. Usually, a hash set (or bit array in your case) can be used to check if a number occurred more than once, but that requires O(n) additional space. For O(1) additional space, we can use the source array itself as a bit array by making some of the numbers in it negative.
For example if the first number in the array is 3, then we make the number at position 3-1 negative. If one of the other numbers in the array is also 3, we can check if the number at position 3-1 is negative.
I don't have any experience with Swift, so I'll try to write a function in pseudocode:
function firstDuplicate(a)
result = -1
for i = 0 to a.count - 1
if a[abs(a[i])-1] < 0 then
result = a[i]
exit for loop
else
a[abs(a[i])-1] = -a[abs(a[i])-1]
// optional restore the negative numbers back to positive
for i = 0 to a.count - 1
if a[i] < 0 then
a[i] = -a[i]
return result

Replace this line
for secondIndex in 0..<a.count
with
for secondIndex in firstIndex..<a.count
There is no requirement of double checking
So Your Final code is
func firstDuplicate(a : [Int]) -> Int {
var duplicateIndexArray = [Int]()
for firstIndex in 0..<a.count {
for secondIndex in firstIndex..<a.count {
if a[firstIndex] == a[secondIndex] && firstIndex != secondIndex {
print(firstIndex, secondIndex)
if !(duplicateIndexArray.contains(firstIndex))
{
duplicateIndexArray.append(secondIndex)
break
}
}
}
}
// Check for duplicacy
if duplicateIndexArray.count > 0
{
print(duplicateIndexArray)
return a[duplicateIndexArray.min()!]
}
return -1
}

func firstDuplicate(input: [Int]) -> Int{
var map : [String : Int] = [:]
var result = -1
for i in 0 ..< input.count {
if map["\(input[i])"] != nil {
result = i
break
}
else {
map["\(input[i])"] = i
}
}
return result
}

Related

Skip to index in for-in loop

Let's say I have a for-in loop like this:
for index in 1...5 {
}
And let's say that I find that, at index 2, I want to skip to index 4 due to some condition. The following does not work:
for index in 1...5 {
if index == 2 {
index = 4
}
}
Because it gives me the following error:
Cannot assign to value: 'index' is a 'let' constant
How can I modify the position of the index to skip to index 4?
In a for loop, you cannot jump the index in real time - that is, if you discover once the loop has started that you need to skip an iteration, you can't. But you can duck out of an iteration with continue. So for example:
var skip = 0
for i in 1...5 {
if i < skip { continue }
print(i)
if i == 2 { skip = 4}
}
In a situation like this, however, you might be happier with a while loop.
var i = 1
while i <= 5 {
print(i)
i += 1
if i == 3 { i = 4 }
}
Another possibility is to unroll the original for loop into a while loop:
var r = (1...5).makeIterator()
while let i = r.next() {
print(i)
if i == 2 { r.next() }
}
All of those are ways of printing 1,2,4,5.

How to print all the digits in a large number of 10 power 25 in swift?

I have been working on a hacker rank problem where I have to print a number which is a factorial of 25. Here is the code I used.
func extraLongFactorials(n: Int) -> Void {
let factorialNumber = factorial(number: n)
var arrayForStorage: [Int] = []
var loop = factorialNumber
while (loop > 0) {
let digit = loop.truncatingRemainder(dividingBy: 10)
arrayForStorage.append(Int(digit))
loop /= 10
}
arrayForStorage = arrayForStorage.reversed()
var returnString = ""
for element in arrayForStorage {
returnString = "\(returnString)\(element)"
}
print(returnString)
}
func factorial(number: Int) -> Double {
if number == 0 || number == 1 {
return 1
} else if number == 2 {
return 2
} else {
return Double(number) * factorial(number: number - 1)
}
}
But when I try to print the factorial number it just prints 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015511210043330982408266888 when it should print
15511210043330985984000000.
I think for a Double number truncatingRemainder(dividingBy: 10) method is not giving me the exact number of the remainder. Because when I tried to print the truncatingRemainder of 15511210043330985984000000 it is giving me as 8. Here is the code.
let number: Double = 15511210043330985984000000
print(number.truncatingRemainder(dividingBy: 10))
So finally I didn't find any solution for the problem of how to split the large number and add it into an array. Looking forward for the solution.
Type Double stores a number as a mantissa and an exponent. The mantissa represents the significant figures of the number, and the exponent represents the magnitude of the number. A Double can only represent about 16 significant figures, and your number has 26 digits, so you can't accurately store 15511210043330985984000000 in a Double.
let number1: Double = 15511210043330985984000000
let number2: Double = 15511210043330985984012345
if number1 == number2 {
print("they are equal")
}
they are equal
You will need another approach to find large factorials like the one shown in this answer.

for loop over odd numbers in swift

I am trying to solve the task
Using a standard for-in loop add all odd numbers less than or equal to 100 to the oddNumbers array
I tried the following:
var oddNumbers = [Int]()
var numbt = 0
for newNumt in 0..<100 {
var newNumt = numbt + 1; numbt += 2; oddNumbers.append(newNumt)
}
print(oddNumbers)
This results in:
1,3,5,7,9,...199
My question is: Why does it print numbers above 100 although I specify the range between 0 and <100?
You're doing a mistake:
for newNumt in 0..<100 {
var newNumt = numbt + 1; numbt += 2; oddNumbers.append(newNumt)
}
The variable newNumt defined inside the loop does not affect the variable newNumt declared in the for statement. So the for loop prints out the first 100 odd numbers, not the odd numbers between 0 and 100.
If you need to use a for loop:
var odds = [Int]()
for number in 0...100 where number % 2 == 1 {
odds.append(number)
}
Alternatively:
let odds = (0...100).filter { $0 % 2 == 1 }
will filter the odd numbers from an array with items from 0 to 100. For an even better implementation use the stride operator:
let odds = Array(stride(from: 1, to: 100, by: 2))
If you want all the odd numbers between 0 and 100 you can write
let oddNums = (0...100).filter { $0 % 2 == 1 }
or
let oddNums = Array(stride(from: 1, to: 100, by: 2))
Why does it print numbers above 100 although I specify the range between 0 and <100?
Look again at your code:
for newNumt in 0..<100 {
var newNumt = numbt + 1; numbt += 2; oddNumbers.append(newNumt)
}
The newNumt used inside the loop is different from the loop variable; the var newNumt declares a new variable whose scope is the body of the loop, so it gets created and destroyed each time through the loop. Meanwhile, numbt is declared outside the loop, so it keeps being incremented by 2 each time through the loop.
I see that this is an old question, but none of the answers specifically address looping over odd numbers, so I'll add another. The stride() function that Luca Angeletti pointed to is the right way to go, but you can use it directly in a for loop like this:
for oddNumber in stride(from:1, to:100, by:2) {
// your code here
}
stride(from:,to:,by:) creates a list of any strideable type up to but not including the from: parameter, in increments of the by: parameter, so in this case oddNumber starts at 1 and includes 3, 5, 7, 9...99. If you want to include the upper limit, there's a stride(from:,through:,by:) form where the through: parameter is included.
If you want all the odd numbers between 0 and 100 you can write
for i in 1...100 {
if i % 2 == 1 {
continue
}
print(i - 1)
}
For Swift 4.2
extension Collection {
func everyOther(_ body: (Element) -> Void) {
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
body(self[iter])
let next = index(after: iter)
if next == end { break }
iter = index(after: next)
}
}
}
And then you can use it like this:
class OddsEvent: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
(1...900000).everyOther{ print($0) } //Even
(0...100000).everyOther{ print($0) } //Odds
}
}
This is more efficient than:
let oddNums = (0...100).filter { $0 % 2 == 1 } or
let oddNums = Array(stride(from: 1, to: 100, by: 2))
because supports larger Collections
Source: https://developer.apple.com/videos/play/wwdc2018/229/

more elegant code for for if in swift

I got a simple code which works and which I am programming in and old fashioned way and I am sure there is a more elegant way of doing this in swift. Here is the code:
var cardsInCompartment1:Int = 0
var cardsInCompartment2:Int = 0
for card in cards{
if card.compartment == 1{
cardsInCompartment1 += 1
print(cardsInCompartment1)
}
if card.compartment == 2{
cardsInCompartment2 += 1
print(cardsInCompartment2)
}
}
I basically got cards in different compartments and now I want to count how many cards are in each compartment.
How about using filter to select the cards you want? Then you can just count them:
let cardsInCompartment1 = cards.filter { $0.compartment == 1 }.count
let cardsInCompartment2 = cards.filter { $0.compartment == 2 }.count
If you have a bunch of compartments, you could store the counts in a dictionary:
var compartmentCounts = [Int:Int]()
cards.forEach {
compartmentCounts[$0.compartment] = (compartmentCounts[$0.compartment] ?? 0) + 1
}
In this case, the key would be the compartment#, and the value would be the card count. Something like [1: 32, 2: 42] if there are 32 and 42 cards in each respective compartment.
Try this:
var cardsInCompartment1:Int = 0
var cardsInCompartment2:Int = 0
for card in cards {
(card.compartment == 1) ? (cardsInCompartment1 += 1) : (cardsInCompartment2 += 1)
}
I think you should store the cardsInCompartment as arrays:
var cardsInCompartment = [0, 0] // you can add more to this array
Then you can just loop through the cards and add the values to the array elements:
for card in cards {
cardsInCompartment[card.compartment - 1] += 1
print(cardsInCompartment[card.compartment - 1])
}
What about a switch statement? Something like this?
var card:Int = 1
var CardsInCompartment:Int = 0
switch (card) {
case 1:
CardsInCompartment += 1
print("CardsInCompartment \(CardsInCompartment)")
case 2:
CardsInCompartment += 2
print("CardsInCompartment \(CardsInCompartment)")
default:
}
Or, use an Array to keep your counts:
var counts = [ 0, 0, 0 ] // create an array of integers, where the Ints in the array represent the count of cards in each compartment
cards.forEach { counts[ $0.compartment ] += 1 } // for each card, increment the count in array corresponding to the compartment of the card. (if card.compartment == 1, increment counts[1], and so on
print("cards in compartment 1 \(counts[1])")
print("cards in compartment 2 \(counts[2])")
(This assumes your only compartments are integers 1 and 2)
I like Aaron Brager's idea which counts values into dictionary. I am using reduce to eliminate mutable dictionary outside the 'loop' (more functional)
let d = cards.reduce([:]) { (d, card) -> [Int:Int] in
var d = d
let s = d[card.compartment] ?? 0
d[card.compartment] = s + 1
return d
}

Simple Swift Fibonacci program crashing (Project Euler 2)

I am trying to solve the second problem on Project Euler. The problem is as follows:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
I think I've written a solution, but when I try to run my code it crashes my Swift playground and gives me this error message:
Playground execution aborted: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
var prev = 0
var next = 1
var num = 0
var sum = 0
for var i = 1; i < 400; i++ {
num = prev + next
if next % 2 == 0 {
sum += next
}
prev = next
next = num
}
print(sum)
The weird thing is, if I set the counter on my loop to less than 93, it works fine. Explicitly setting the variable names to Double does not help. Anyone know what's going on here?
There is nothing weird about this at all. Do you know how large the 400 fibonacci number is?
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
Swift Int64 or UInt64 simply cannot handle that large of a number. The later can go up to 18446744073709551615 at max - not even close.
If you change your variables to be doubles it works but will be inaccurate:
var prev : Double = 0
var next : Double = 1
var num : Double = 0
var sum : Double = 0
will yield
2.84812298108489e+83
which is kind of close to the actual value of
1.76e+83
Luckily you do not need to get values that big. I would recommend not writing a for loop but a while loop that calculates the next fibonacci number until the break condition is met whose values do not exceed four million.
The Fibonacci numbers become very large quickly. To compute large Fibonacci numbers, you need to implement some kind of BigNum. Here is a version the makes a BigNum that is implemented internally as an array of digits. For example, 12345 is implemented internally as [1, 2, 3, 4, 5]. This makes it easy to represent arbitrarily large numbers.
Addition is implemented by making the two arrays the same size, then map is used to add the elements, finally the carryAll function restores the array to single digits.
For example 12345 + 67:
[1, 2, 3, 4, 5] + [6, 7] // numbers represented as arrays
[1, 2, 3, 4, 5] + [0, 0, 0, 6, 7] // pad the shorter array with 0's
[1, 2, 3, 10, 12] // add the arrays element-wise
[1, 2, 4, 1, 2] // perform carry operation
Here is the implementation of BigNum. It is also CustomStringConvertible which makes it possible to print the result as a String.
struct BigNum: CustomStringConvertible {
var arr = [Int]()
// Return BigNum value as a String so it can be printed
var description: String { return arr.map(String.init).joined() }
init(_ arr: [Int]) {
self.arr = carryAll(arr)
}
// Allow BigNum to be initialized with an `Int`
init(_ i: Int = 0) {
self.init([i])
}
// Perform the carry operation to restore the array to single
// digits
func carryAll(_ arr: [Int]) -> [Int] {
var result = [Int]()
var carry = 0
for val in arr.reversed() {
let total = val + carry
let digit = total % 10
carry = total / 10
result.append(digit)
}
while carry > 0 {
let digit = carry % 10
carry = carry / 10
result.append(digit)
}
return result.reversed()
}
// Enable two BigNums to be added with +
static func +(_ lhs: BigNum, _ rhs: BigNum) -> BigNum {
var arr1 = lhs.arr
var arr2 = rhs.arr
let diff = arr1.count - arr2.count
// Pad the arrays to the same length
if diff < 0 {
arr1 = Array(repeating: 0, count: -diff) + arr1
} else if diff > 0 {
arr2 = Array(repeating: 0, count: diff) + arr2
}
return BigNum(zip(arr1, arr2).map { $0 + $1 })
}
}
// This function is based upon this question:
// https://stackoverflow.com/q/52975875/1630618
func fibonacci(to n: Int) {
guard n >= 2 else { return }
var array = [BigNum(0), BigNum(1)]
for i in 2...n {
array.append(BigNum())
array[i] = array[i - 1] + array[i - 2]
print(array[i])
}
}
fibonacci(to: 400)
Output:
1
2
3
5
8
...
67235063181538321178464953103361505925388677826679492786974790147181418684399715449
108788617463475645289761992289049744844995705477812699099751202749393926359816304226
176023680645013966468226945392411250770384383304492191886725992896575345044216019675