Can't reassign the value of a variable in Kotlin - loops

I am new to Kotlin and I am trying to write a function to convert a number to Roman Numerals; however, I am unable to set the values of "numberBeingChecked" and "romanNumeral" from inside the loop, would you know why?
fun encode(num: Int): String {
var numberBeingChecked: Int = num
var romanNumeral: String = ""
myLoop# while (numberBeingChecked > 0) {
when (numberBeingChecked) {
0 -> {
return romanNumeral
}
in 1..4 -> {
romanNumeral + "I"
numberBeingChecked - 1
break#myLoop
}
in 5..9 -> {
romanNumeral + "V"
numberBeingChecked - 5
break#myLoop
}
in 10..49 -> {
romanNumeral + "X"
numberBeingChecked - 10
break#myLoop
}
in 50..99 -> {
romanNumeral + "L"
numberBeingChecked - 50
break#myLoop
}
in 100..499 -> {
romanNumeral + "C"
numberBeingChecked - 100
break#myLoop
}
in 500..999 -> {
romanNumeral + "D"
numberBeingChecked - 500
break#myLoop
}
else -> {
romanNumeral + "M"
numberBeingChecked - 1000
break#myLoop
}
}
}
return romanNumeral
}
fun main(args: Array<String>) {
encode(0)
encode(1)
encode(21)
encode(2008)
encode(1666)
}
Please let me know if I am doing something wrong, your help will be appreciated.
Kind regards

You don't really reassign these variables in your code. numberBeingChecked - 5 expression does not decrease the value of numberBeingChecked. It just subtracts 5 from numberBeingChecked and does nothing with the result. To decrease numberBeingChecked by 5 you need to use -= operator: numberBeingChecked -= 5. Same applies to romanNumeral, e.g.: romanNumeral += "V".
Another bug in your code is that you always break from the loop after the first step. You should only get out of the loop when numberBeingChecked is zero, so you really need to remove all these break instructions.
Additionally, you should avoid creating strings by concatenating multiple times, because it results in copying the whole string with each step. Instead, create a StringBuilder, append to it and at the last step invoke toString() on it to get a result.
The resulting code looks like this:
var numberBeingChecked: Int = num
var romanNumeral = StringBuilder()
while (numberBeingChecked > 0) {
when (numberBeingChecked) {
in 1..4 -> {
romanNumeral.append("I")
numberBeingChecked -= 1
}
...
}
}
return romanNumeral.toString()
We can simplify it even further with buildString() utility which helps us create the StringBuilder and convert it to a string:
var numberBeingChecked: Int = num
return buildString {
while (numberBeingChecked > 0) {
when (numberBeingChecked) {
in 1..4 -> {
append("I")
numberBeingChecked -= 1
}
...
}
}
}
However, the latter uses some advanced features of Kotlin, it is harder to explain/understand how it works internally and therefore it may not be the best for learning purposes.

You're not performing an assignment. You need either an equals sign as i=i+1, or a shorthand like i++.

Related

Beginner`s Kotlin question. Operating on the array size

I am following the numerous guides from this website: https://www.w3schools.com/kotlin/kotlin_arrays.php
I am experimenting along the way with my own ideas and such.
For the section detailing how to access elements in an array, I decided that I want to create an output that uses correct grammar in English. I want the list to be displayed as such:
"Volvo, BMW, Ford and Audi."
How can I achieve this? I`m of course an absolute beginner.
Thank you!
fun main()
{
val cars = arrayOf("Volvo", "BMW", "Ford", "Mazda")
println(cars[3]) // Prints "Mazda"
if ("Mazda" in cars)
{
println("It exists!")
}
else
{
println("It doesn`t exist!")
}
cars[3] = "Audi"
println(cars[3])
if ("Mazda" in cars)
{
println("It exists!")
}
else
{
println("It doesn`t exist!")
}
println("There are " + cars.size + " elements in this array:")
for (w in cars) // The dilemma begins from here
while (w < cars.size - 1)
{
print(w + ", ")
}
else
{
print(w + "and " + cars[3] + ".")
}
}
#Tenfour04
Thank you for your post.
Here is what I settled at before your last post
for ((i, w) in cars.withIndex())
{
if (i < cars.size - 1)
{
print(w + ", ")
}
else
{
for (i in 0..cars.size-4)
print("and " + cars[3])
}
}
which gave me the output of
Volvo, BMW, Ford, and Audi
After I`ve corrected it with your solution, thank you very much.
The output is still the same:
Volvo, BMW, Ford, and Audi
Now I am wondering how I can remove the third comma. The one after "Ford", so that the final output would be:
Volvo, BMW, Ford and Audi
while is a loop with a condition (in the parentheses after it). That's not what you want. And in your comment, you replaced for with println, which doesn't make sense.
And your else branch makes the mistake of using both w and cars[3] in you final output, two different ways of getting the name of the last car, so it appears twice in your output.
Since you are using both the elements of the array and their indices, you should iterate both at once using withIndex, like this.
Here's how you can iterate both the indices and the cars so you can do an if condition on the index (position) in the list of each item.
for ((i, w) in cars.withIndex()) {
if (i < cars.size - 1)
{
print(w + ", ")
}
else
{
print("and " + w + ".")
}
}
But it's cleaner to use string templates like:
for ((i, w) in cars.withIndex()) {
if (i < cars.size - 1)
{
print("$w, ")
}
else
{
print("and $w.")
}
}
When you have short single function calls in each branch of if/else, it sometimes is cleaner looking code to use when instead:
for ((i, w) in cars.withIndex()) {
when {
i < cars.size - 1 -> print("$w, ")
else -> print("and $w.")
}
}
Or for a completely different strategy, you could iterate all but the last item by dropping it, and then use just the last item:
for (car in cars.dropLast()) {
println("$car, ")
}
print("and ${cars.last()}")
For your comment in your own answer, you can use a when statement with three conditions:
for ((i, w) in cars.withIndex()) {
when {
i < cars.size - 2 -> print("$w, ")
i == cars.size - 2 -> print("$w ")
else -> print("and $w.")
}
}

Performance of moving zeros to the end of an array programming exercise

I wonder why my solution to this LeetCode "Move Zeros" problem is slower than the majority of other submissions. Is there a better way to approach this problem to make it faster?
The question is as follows:
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements. You must do this in-place without making a copy of the array.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
This is my solution:
func moveZeroes(_ nums: inout [Int]) {
var index = 0
for (i,n) in nums.enumerated()
{
if n != 0
{
nums[index] = n
index += 1
}
}
while index < nums.count
{
nums[index] = 0
index += 1
}
}
LeetCode gives me these statistics:
Runtime: 52 ms, faster than 40.50% of Swift online submissions for Move Zeroes.
Memory Usage: 19.4 MB, less than 13.33% of Swift online submissions for Move Zeroes.
EDIT 1:
If I approach the problem as follows, it does not move the zeros at the end,
EDIT 2:
Here is 36ms in-place solution for you :
class Solution {
func moveZeroes(_ nums: inout [Int]) {
if nums.count < 2 {
return
}
var j = 0
while j < nums.count, nums[j] != 0 {
j += 1
}
if j < nums.count - 1 {
for i in j+1..<nums.count {
if nums[i] != 0 {
nums.swapAt(i, j)
j += 1
}
}
}
}
}
From what I can see, it's likely other submissions are doing this
Check and count 0's in string
Remove 0's
Replace number of 0's at the end of the string
A logical method no doubt, but I'd say yours just picks the basic needs of the challenge and goes for it.
I would personally use:
input = input.filter { $0 != 0 } + input.filter { $0 == 0 }
which can be simplified to one pass:
let nonZeros = input.filter { $0 != 0 }
input = nonZeros + Array(repeating: 0, count: input.count - nonZeros.count)
EDIT: The simplest version without creating a new array would be some primitive version of bubble sort, e.g.:
var numZeros = 0
// iterate array from start to end
for (offset, element) in input.enumerated() {
if element == 0 {
// count every zero
numZeros += 1
} else if numZeros > 0 {
// move every non-zero element left
input[offset - numZeros] = element
// replace with zero
input[offset] = 0
}
}
Another approach is the half-stable-partition algorithm. The benefit is the items are swapped rather than removed and inserted/appended.
Half-stable means the order of the left side of the split point is preserved.
extension Array {
mutating func halfStablePartition(indexes : IndexSet) { // code is O(n)
guard var i = indexes.first, i < count else { return }
var j = index(after: i)
var k = indexes.integerGreaterThan(i) ?? endIndex
while j != endIndex {
if k != j { swapAt(i, j); formIndex(after: &i) }
else { k = indexes.integerGreaterThan(k) ?? endIndex }
formIndex(after: &j)
}
}
}
var input = [0,1,0,3,12]
let indices = IndexSet(input.indices.filter{input[$0] == 0})
input.halfStablePartition(indexes: indices)
Swift 4.2 or later using removeAll mutating method:
Mutating the input:
class Solution {
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.removeAll {
if $0 == 0 {
counter += 1
return true
}
return false
}
nums += repeatElement(0, count: counter)
}
}
A similar approach for Swift 4.1 or earlier
func moveZeroes(_ nums: inout [Int]) {
var counter = 0
nums.indices.reversed().forEach {
if nums[$0] == 0 {
counter += 1
nums.remove(at: $0)
}
}
nums += repeatElement(0, count: counter)
}
var input = [0,1,0,3,12]
moveZeroes(&input)
input // [1, 3, 12, 0, 0]
Non mutating approach:
func moveZeroes(_ nums: [Int]) -> [Int] {
var counter = 0
return nums.filter {
if $0 == 0 { counter += 1 }
return $0 != 0
} + repeatElement(0, count: counter)
}
let input = [0,1,0,3,12]
let zerosMoved = moveZeroes(input)
zerosMoved // [1, 3, 12, 0, 0]
For modifying array in place, and keeping it:
O(n) for Time Complexity
O(1) for Space Complexity
Cracked my head way to long for this one. The cleanest way if you swap element that is NOT zero:
func moveZeroes(_ nums: inout [Int]) {
// amount of swaps, will be used a as reference for next swap index
var j = 0
for (i, e) in nums.enumerated() {
if e != 0 {
nums.swapAt(j, i)
j += 1
}
}
}
One fast solution is to shift non-zero elements to the left by the amount of zeros encountered until then:
func moveZeroes(_ nums: inout [Int]) {
var offset = 0
for i in 0..<nums.count {
if nums[i] == 0 { offset += 1 }
else { nums.swapAt(i, i-offset) }
}
}
This solution takes exactly N steps, and at each step we either perform an addition, or a swap, which are both quite fast.
Your solution, on the other hand required two iterations, resulting in 2*N steps, which is why it was slower than other solutions.

Get median of array

I have an array that looks like this:
let arr = [1,2,3,4,5,6,7,8,9]
I know you can get min and max by:
let min = arr.min()
let max = arr.max()
But how do you get the median?
To get the median you can use the following:
let median = arr.sorted(by: <)[arr.count / 2]
In your case it will return 5.
As #Nirav pointed out [1,2,3,4,5,6,7,8] will return 5 but should return 4.5.
Use this instead:
func calculateMedian(array: [Int]) -> Float {
let sorted = array.sorted()
if sorted.count % 2 == 0 {
return Float((sorted[(sorted.count / 2)] + sorted[(sorted.count / 2) - 1])) / 2
} else {
return Float(sorted[(sorted.count - 1) / 2])
}
}
Usage:
let array = [1,2,3,4,5,6,7,8]
let m2 = calculateMedian(array: array) // 4.5
The median is defined as the number in the middle of the sequence. If there is not one middle number, then it's the average of the two middle numbers.
extension Array where Element == Int {
func median() -> Double {
let sortedArray = sorted()
if count % 2 != 0 {
return Double(sortedArray[count / 2])
} else {
return Double(sortedArray[count / 2] + sortedArray[count / 2 - 1]) / 2.0
}
}
}
Note that if the array is empty, the median is undefined. So a safe median function returns an optional, just like the min() and max() built-in methods do.
extension Array where Element == Int {
func median() -> Double? {
guard count > 0 else { return nil }
let sortedArray = self.sorted()
if count % 2 != 0 {
return Double(sortedArray[count/2])
} else {
return Double(sortedArray[count/2] + sortedArray[count/2 - 1]) / 2.0
}
}
}
With that defined, you can write:
if let median = arr.median() {
// do something
}
If someone (like me) likes two*-liners:
let sorted = arr.sorted(by: <)
let median = Double(sorted[arr.count/2] + sorted.reversed()[arr.count/2])/2.0
Algorithms that use sorted take O(n log n) time. That's typically not a problem for 9 numbers, but if your array is large, use an algorithm that completes in O(n) time. An example is this k-th largest element algorithm. It recursively partitions the array, but doesn’t have to go through all the work to sort it, so it’s much faster.

Executing bad excess on receiving a function return array?

What is the problem with the following function to receive and execute:
func FibFast(num: Int) -> Array<Int> {
var fib_arr = [Int](num)
if num < 1 {
fib_arr[0] = 0
return fib_arr
} else if num < 2 {
fib_arr[1] = 1
return fib_arr
}else {
for var i = 2; i < num; i++ {
fib_arr[i] = fib_arr[i-1] + fib_arr[i-2]
}
return fib_arr
}
}
when I am trying to receive the array like:
var newArray = FibFast(10)
it's producing a bad execution.
You are attempting to subscript the array with indexes that don't exist, similarly in your else case you are trying to subscript to an index of 2 when the array is empty.
the line var fib_arr = [Int]() creates an empty array on integers. when you use fib_arr[0] = 0 you are trying to assign the value at index 0 to have a value of 0 but no value currently exists. I would recommend using the append(_) method ie. fib_arr.append(0).
Also when you pass in a value of 10 or anything that is 2 or more as the num parameter your if-else if-else statement is executing the for loop where you are attempting to access the index of 0 and 1 which have not been set as the earlier statements were never executed as they were skipped by the if-else if-else block.
The for loop and assign the values with subscripts is very indicative of the fact that you've probably learnt a different language before swift. You should note that the classic c-style for loop you are trying to use will be removed from swift soon so its better to get in the habbit of no using it. Ive rewritten your code as close to the way you wrote yours, please don't hesitate to ask if you have any questions about it.
func fib(num: Int) -> [Int] {
var fib_arr = [Int]()
if num == 0 {
fib_arr.append(0)
} else if num == 1 {
fib_arr.append(0)
fib_arr.append(1)
} else {
fib_arr = [0, 1]
for i in 2..<num {
fib_arr.append(fib_arr[i - 1] + fib_arr[i - 2])
}
}
return fib_arr
}
The answer proposed by Blake Lockley can also be coded like this:
func FibFast(num: Int) -> Array<Int> {
var fib_arr = [Int]() // empty array
if num < 1 {
fib_arr += [0] // add element [0]
return fib_arr
} else if num < 2 {
fib_arr += [0] // add element [0]
fib_arr += [1] // add element [1]
return fib_arr
}else {
fib_arr = [0, 1] // init with [0, 1]
for var i = 2; i < num; i++ {
// add computed element
fib_arr += [fib_arr[i-1] + fib_arr[i-2]]
}
return fib_arr
}
}

Project Euler 7 Scala Problem

I was trying to solve Project Euler problem number 7 using scala 2.8
First solution implemented by me takes ~8 seconds
def problem_7:Int = {
var num = 17;
var primes = new ArrayBuffer[Int]();
primes += 2
primes += 3
primes += 5
primes += 7
primes += 11
primes += 13
while (primes.size < 10001){
if (isPrime(num, primes)) primes += num
if (isPrime(num+2, primes)) primes += num+2
num += 6
}
return primes.last;
}
def isPrime(num:Int, primes:ArrayBuffer[Int]):Boolean = {
// if n == 2 return false;
// if n == 3 return false;
var r = Math.sqrt(num)
for (i <- primes){
if(i <= r ){
if (num % i == 0) return false;
}
}
return true;
}
Later I tried the same problem without storing prime numbers in array buffer. This take .118 seconds.
def problem_7_alt:Int = {
var limit = 10001;
var count = 6;
var num:Int = 17;
while(count < limit){
if (isPrime2(num)) count += 1;
if (isPrime2(num+2)) count += 1;
num += 6;
}
return num;
}
def isPrime2(n:Int):Boolean = {
// if n == 2 return false;
// if n == 3 return false;
var r = Math.sqrt(n)
var f = 5;
while (f <= r){
if (n % f == 0) {
return false;
} else if (n % (f+2) == 0) {
return false;
}
f += 6;
}
return true;
}
I tried using various mutable array/list implementations in Scala but was not able to make solution one faster. I do not think that storing Int in a array of size 10001 can make program slow. Is there some better way to use lists/arrays in scala?
The problem here is that ArrayBuffer is parameterized, so what it really stores are references to Object. Any reference to an Int is automatically boxed and unboxed as needed, which makes it very slow. It is incredibly slow with Scala 2.7, which uses a Java primitive to do that, which does it very slowly. Scala 2.8 takes another approach, making it faster. But any boxing/unboxing will slow you down. Furthermore, you are first looking up the ArrayBuffer in the heap, and then looking up again for java.lang.Integer containing the Int -- two memory accesses, which makes it way slower than your other solution.
When Scala collections become specialized, it should be plenty faster. Whether it should be enough to beat your second version or not, I don't know.
Now, what you may do to get around that is to use Array instead. Because Java's Array are not erased, you avoid the boxing/unboxing.
Also, when you use for-comprehensions, your code is effectively stored in a method which is called for each element. So you are also making many method calls, which is another reason this is slower. Alas, someone wrote a plugin for Scala which optimizes at least one case of for-comprehensions to avoid that.
Using Array should make it work in about zero seconds with the right algorithm. This, for example, takes about 7 milliseconds on my system:
class Primes(bufsize: Int) {
var n = 1
val pbuf = new Array[Int](bufsize max 1)
pbuf(0) = 2
def isPrime(num: Int): Boolean = {
var i = 0
while (i < n && pbuf(i)*pbuf(i) <= num) {
if (num % pbuf(i) == 0) return false
i += 1
}
if (pbuf(i)*pbuf(i) < num) {
i = pbuf(i)
while (i*i <= num) {
if (num % i == 0) return false
i += 2
}
}
return true;
}
def fillBuf {
var i = 3
n = 1
while (n < bufsize) {
if (isPrime(i)) { pbuf(n) = i; n += 1 }
i += 2
}
}
def lastPrime = { if (n<bufsize) fillBuf ; pbuf(pbuf.length-1) }
}
object Primes {
def timedGet(num: Int) = {
val t0 = System.nanoTime
val p = (new Primes(num)).lastPrime
val t1 = System.nanoTime
(p , (t1-t0)*1e-9)
}
}
Result (on second call; first has some overhead):
scala> Primes.timedGet(10001)
res1: (Int, Double) = (104743,0.00683394)
I think you have to think out of the box :)
Because the problem is manageable, you can use Sieve of Eratosthenes to solve it very efficiently.
Here's a recursive solution (using the isPrime function from your first solution). It seems to be good Scala style to prefer immutability (i.e. to try not to use vars) so I've done that here (in fact there are no vars or vals!). I don't have a Scala installation here though so can't tell if this is actually any quicker!
def problem_7:Int = {
def isPrime_(n: Int) = (n % 6 == 1 || n % 6 == 5) && isPrime(n)
def process(n: Int, acc: List[Int]): Int = {
if (acc.size == 10001) acc.head
else process(n+1, if isPrime_(n) n :: acc else acc)
}
process(1, Nil)
}

Resources