0

Hi I am writing an app in kotlin and need to decompose a number into powers of 2.

I have already done this in c#, PHP and swift but kotlin works differently somehow.

having researched this I believe it is something to do with the numbers in my code going negative somewhere and that the solution lies in declaring one or more of the variable as "Long" to prevent this from happening but i have not been able to figure out how to do this.

here is my code:

    var salads = StringBuilder()
    var value = 127
    var j=0
    while (j < 256) {

        var mask = 1 shl j
        if(value != 0 && mask != 0)  {

            salads.append(mask)
            salads.append(",")

        }
        j += 1
    }

    // salads = (salads.dropLast()) // removes the final ","
    println("Salads = $salads")

This shoud output the following: 1,2,4,8,16,32,64 What I actually get is:

1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,

Any ideas?

Drewby80
  • 31
  • 6
  • Can you mark my answer as "accepted" please? – Andrew Koster Aug 15 '19 at 20:14
  • Can you clarify what you mean by saying you want to decompose a number into powers of 2. Does that mean you want to write the number as the sum of powers of 2? Or do you have something else in mind? – Wilf Rosenbaum Aug 15 '19 at 20:50
  • My system stores options that have been chosen for food being ordered on a click and collect system, as there are many options that can be configured in different combinations each permutation is referanced by a power 2 number i.e. 1,2,4,8,16,32 etc these represent salads, fillings sauce options chosen for the order etc, once all of these items are picked they are then stored as a single integer in the database so lettuce-1 cucumber-4 mayonnaise-16 chosen are stored as the value 20 in the DB thus the system only requires a single field as an integer to store options and is high performance – Drewby80 Aug 15 '19 at 21:24
  • OK, good (but I think you meant "21" where you wrote "20" above, right?). So what you want your new function to do is to take in the value 21 and return {1,4,16}, the powers of 2 that 21 is "decomposed" into. Correct? That is what the "Answer" for your swift question does, but it is not what the accepted answer for the kotlin function does. The accepted answer for the kotlin function just returns all of the powers of 2 that are less than the input value. This happens to work if value is of the form 2^n-1 but not otherwise. ie the kotlin answer will work for input 127 but not 126 or 129. – Wilf Rosenbaum Aug 15 '19 at 22:08
  • Oops yes 21 i have now figured this out my working solution is below, this is probably not the most elegant solution but it works just fine. if you caould suggest an improvment in this that would be great! – Drewby80 Aug 15 '19 at 22:27

4 Answers4

0

This works for the one input that you specified, at the very least:

fun powersOfTwo(value :Long): String {

    val result = ArrayList<String>()

    var i = 0
    var lastMask = 0

    while (lastMask < value) {

        val mask = 1 shl i

        if (value != 0.toLong() && mask < value) {

            result.add(mask.toString())
        }

        lastMask = mask
        i += 1
    }

    return result.joinToString(",")
}

Ran it in a unit test:

@Test
fun addition_isCorrect() {

    val result = powersOfTwo(127)

    assertEquals("1,2,4,8,16,32,64", result)
}

Test passed.

Andrew Koster
  • 1,550
  • 1
  • 21
  • 31
  • This is only going to work for input values that are of the form 2^n-1 where n is an integer. Try it for value=129 instead of 127. – Wilf Rosenbaum Aug 15 '19 at 19:46
  • Yeah. An input of 129 gives you an output of `1,2,4,8,16,32,64,128`. The problem statement doesn't indicate that we care about remainders. If we do, Drewby80 would have to specify how we care about them. – Andrew Koster Aug 15 '19 at 20:17
  • Drewby80, you asked a very similar question about how to implement this same functionality in swift. The solution to the swift question will provide decidedly different results than this kotlin implementation for all values not of the form 2^n-1. – Wilf Rosenbaum Aug 15 '19 at 20:57
0

Hi I finally managed to get this working properly:

fun decomposeByTwo(value :Int): String {

val result = ArrayList<String>()
var value = value
var j = 0
while (j < 256) {

    var mask = 1 shl j
    if ((value and mask) != 0) {
        value -= mask
        result.add(mask.toString())

    }
    j += 1
}

return result.toString()
}

I hope this helps someone trying to get a handle on bitwise options!

Drewby80
  • 31
  • 6
  • I haven't tested this but it looks like it should work. In my original response i said that you have to AND together value and mask to determine if the j-th bit of value is set, and that is what you are doing now. Note that there is no reason for you to test all the way up to j<256. Kotlin Ints have 32 bits, and you are passing value in as an Int, so you just need to test up to j<32. You may want to change the type of the "value" parameter to be ULong, which would give you 64 bits . – Wilf Rosenbaum Aug 15 '19 at 23:05
  • Also your mask calculation, as it is now, is going to be 0 when j>32. "1L shl j" should work for j < 64. Add a line to print out the value of mask and j on every iteration as soon as you calculate the mask, for debugging. – Wilf Rosenbaum Aug 15 '19 at 23:08
  • Wow, you accepted your own answer after I solved the problem for you. Classy. – Andrew Koster Aug 18 '19 at 02:23
  • Yeah, sure, let's say that I couldn't have done it without you. – Andrew Koster Aug 19 '19 at 08:07
0

You can get a list of all powers of two that fit in Int and test each of them for whether the value contains it with the infix function and:

val value = 126

val powersOfTwo = (0 until Int.SIZE_BITS).map { n -> 1 shl n }
println(powersOfTwo.filter { p -> value and p != 0}.joinToString(","))
// prints: 2,4,8,16,32,64

See the entire code in Kotlin playground: https://pl.kotl.in/f4CZtmCyI

Ilya
  • 21,871
  • 8
  • 73
  • 92
-1

Somehow you want to do the "bitwise AND" of "value" and "mask" to determine if the j-th bit of "value" is set. I think you just forgot that test in your kotlin implementation.

Wilf Rosenbaum
  • 518
  • 3
  • 10