diff --git a/exercises/practice/eliuds-eggs/.approaches/config.json b/exercises/practice/eliuds-eggs/.approaches/config.json new file mode 100644 index 00000000000..22b9f975c58 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/config.json @@ -0,0 +1,22 @@ +{ + "introduction": { + "authors": ["yrahcaz7"], + "contributors": [] + }, + "approaches": [ + { + "uuid": "27fb20ed-a4c2-4f73-a8fc-86ba384c7b35", + "slug": "parameter-modification", + "title": "Modify the Parameter in a Loop", + "blurb": "Modify the parameter in a while-loop to calculate the number of eggs.", + "authors": ["yrahcaz7"] + }, + { + "uuid": "65fd717c-0e50-444b-a4b2-b51862f1f810", + "slug": "no-parameter-modification", + "title": "Loop Without Modifying the Parameter", + "blurb": "Loop over the bits without modifying the parameter to determine the number of eggs.", + "authors": ["yrahcaz7"] + } + ] +} diff --git a/exercises/practice/eliuds-eggs/.approaches/introduction.md b/exercises/practice/eliuds-eggs/.approaches/introduction.md new file mode 100644 index 00000000000..35ddfde82e3 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/introduction.md @@ -0,0 +1,72 @@ +# Introduction + +There are many Pythonic approaches to solving the Eliud's Eggs exercise: + +- Using a `while-loop`, modifying the parameter on each iteration +- Looping over every binary digit _without_ modifying the parameter +- Converting the `int` to a binary string and counting the ones + +There are also some approaches that aren't recommended: + +- Using the bit-count functionality from the Python standard library, as the instructions forbid it +- Breaking up the proccess into many functions, overcomplicating the solution + + +## General guidance + +The goal of the Eliud's Eggs exercise is to count the number of ones in the binary representation of a number. +In essence, this requires you to loop over each bit (binary digit) of the number in some way. + +The approaches below represent categories of the most common ways of accomplishing this. + + +## Approach: Modifying the Parameter in a `while-loop` + +```python +def egg_count(display_value): + eggs = 0 + while display_value: + eggs += display_value % 2 + display_value //= 2 + return eggs +``` + +This approach uses a `while-loop` to count up all of the ones. +In the loop, we increment `eggs` by `display_value % 2`. +This adds the least significant bit (the rightmost digit in the binary representation) of `display_value` to `eggs`. + +Next, we divide `display_value` by `2`, discarding any remainder. +This essentially removes the least significant bit of `display_value`, setting up `display_value` for processing the next bit. + +The loop repeats until `display_value` reaches `0` (which indicates that we have no more bits to check), and then we return `eggs`. + +To see more variations of this solution, see the [modify the parameter in a loop][approach-parameter-modification] approach. + + +## Approach: Looping Without Modifying the Parameter + +```python +from math import ceil, log2 + +def egg_count(display_value): + eggs = 0 + for bit_position in range(ceil(log2(display_value + 1))): + eggs += (display_value >> bit_position) & 1 + return eggs +``` + +This solution uses a `for-loop` with `range()` to iterate over all of the bits in `display_value`. +To determine how many bits `display_value` has, this solution imports `ceil` and `log2` from the `math` module. +It then feeds this number into `range()` to make the `for-loop` iterate over all the `bit_position`s. + +For each `bit_position`, we determine the value of the bit at that position by using the [right-shift operator][right-shift-operator] and the bitwise AND operator. +Once we determine the bit's value, we increment `eggs` by that number. + +After the loop ends, we know that we have checked all of the bits in `display_value`, thus we return `eggs`. + +For more details and variations, read the [loop without modifying the parameter][approach-no-parameter-modification] approach. + + +[approach-parameter-modification]: https://exercism.org/tracks/python/exercises/eliuds-eggs/approaches/parameter-modification +[approach-no-parameter-modification]: https://exercism.org/tracks/python/exercises/eliuds-eggs/approaches/no-parameter-modification +[right-shift-operator]: https://www.geeksforgeeks.org/software-engineering/right-shift-operator-in-programming/ diff --git a/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/content.md b/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/content.md new file mode 100644 index 00000000000..a2a93c01b37 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/content.md @@ -0,0 +1,91 @@ +# Loop Without Modifying the Parameter + +```python +from math import ceil, log2 + +def egg_count(display_value): + eggs = 0 + for bit_position in range(ceil(log2(display_value + 1))): + eggs += (display_value >> bit_position) & 1 + return eggs +``` + +This approach uses a loop with `range()` to iterate over all of the bits in `display_value`. + +To determine how many bits `display_value` has, this solution imports `ceil` and `log2` from the `math` module. +We then calculate the base 2 logarithm of `display_value` (plus 1) and round it up. +(Rounding up is neccessary because `range()` excludes the value of `` from its returned values.) + +Once we have the bit length of `display_value`, we feed this number into `range()` to make the `for-loop` iterate over all of `display_value`'s `bit_position`s. + +For each `bit_position`, we determine the value of the bit at that position by using the [right-shift operator][right-shift-operator] and the bitwise AND operator. +We do this by right-shifting `display_value` by `bit_position`, making the bit at `bit_position` become the least significant bit. +Then we use the bitwise AND operator with `1` to remove all bits that are not the least significant bit. + +~~~~exercism/note +You could also calculate the bit's value by using arithmetic operators instead of bitwise ones: + +```python +eggs += (display_value // (2 ** bit_position)) % 2 +``` +~~~~ + +Once we determine the bit's value, we increment `eggs` by that number. + +After the loop ends, we know that we have checked all of the bits in `display_value`, thus we return `eggs`. + + +## Variation #1: Using an `if` Statement + +```python +from math import ceil, log2 + +def egg_count(display_value): + eggs = 0 + for bit_position in range(ceil(log2(display_value + 1))): + if display_value & (1 << bit_position): + eggs += 1 + return eggs +``` + +In this variant, the loop uses an `if` statement to check if the digit at `display_value` is `1`. + + +## Variation #2: Using `sum()` with a Generator Expression + +```python +from math import ceil, log2 + +def egg_count(display_value): + return sum( + (display_value >> bit_position) & 1 + for bit_position in range(ceil(log2(display_value + 1))) + ) +``` + +This variant is actually a one-liner, it is just split up here for readability. +Here, we replace the `for-loop` with a [generator expression][generator-expression] and use `sum()` to collect the values into the result. + + +## Variation #3: Manually Tracking the Place Value + +```python +def egg_count(display_value): + eggs = 0 + place_value = 1 + while place_value <= display_value: + if display_value & place_value: + eggs += 1 + place_value <<= 1 + return eggs +``` + +This variant avoids `import`s by manually tracking the `place_value` of the current bit position. +This way, the `while-loop` can end when `place_value` becomes greater than `display_value`. + +The operations in the loop are rather similar to the "using an `if` statement" variant. +The main differences are not having to calculate the `place_value` from the bit position, and having to manually progress the iteration by left-shifting `place_value` by `1` (which is the same as multiplying `place_value` by `2`). + + +[generator-expression]: https://dbader.org/blog/python-generator-expressions +[right-shift-operator]: https://www.geeksforgeeks.org/software-engineering/right-shift-operator-in-programming/ diff --git a/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/snippet.txt b/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/snippet.txt new file mode 100644 index 00000000000..b2a163f2f28 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/no-parameter-modification/snippet.txt @@ -0,0 +1,7 @@ +from math import ceil, log2 + +def egg_count(display_value): + eggs = 0 + for bit_position in range(ceil(log2(display_value + 1))): + eggs += (display_value >> bit_position) & 1 + return eggs \ No newline at end of file diff --git a/exercises/practice/eliuds-eggs/.approaches/parameter-modification/content.md b/exercises/practice/eliuds-eggs/.approaches/parameter-modification/content.md new file mode 100644 index 00000000000..b1765f865d3 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/parameter-modification/content.md @@ -0,0 +1,80 @@ +# Modify the Parameter in a Loop + +```python +def egg_count(display_value): + eggs = 0 + while display_value: + eggs += display_value % 2 + display_value //= 2 + return eggs +``` + +This approach uses a `while-loop` to count up all of the ones. +In the loop, we increment `eggs` by `display_value % 2`. +This adds the least significant bit (the rightmost digit in the binary representation) of `display_value` to `eggs`. + +Next, we divide `display_value` by `2`, discarding any remainder. +This essentially removes the least significant bit of `display_value`, setting up `display_value` for processing the next bit. + +The loop repeats until `display_value` reaches `0` (which indicates that we have no more bits to check), and then we return `eggs`. + + +## Variation #1: Using Boolean Operators + +```python +def egg_count(display_value): + eggs = 0 + while display_value > 0: + if display_value % 2 == 1: + eggs += 1 + display_value //= 2 + return eggs +``` + +This is essentially just a more verbose formulation of the previous version. +Instead of relying on Python converting `int`s to `bool`s, this solution manually compares `display_value` to `0`. +It also uses an `if` statement to check if `eggs` should be incremented by `1`, instead of directly using the result of `display_value % 2`. + +Even though this variant is more verbose than the others, some may consider it to be more readable. + + +## Variation #2: Using Bitwise Operators + +```python +def egg_count(display_value): + eggs = 0 + while display_value > 0: + eggs += display_value & 1 + display_value >>= 1 + return eggs +``` + +This variant replaces the modulo (`%`) and floor division (`//`) with [bitwise operators][bitwise-operators]. +`&` is the bitwise AND operator, which results in a number whose binary representation only has ones where _both_ of its arguments has ones. +(All other bits are zeros.) + +For example, if we used the numbers `3` (`11` in binary) and `1` (`1` in binary), we get `1`: + +```python +0b011 & 0b001 +#=> 0b001 +``` + +This is because the only bit in both numbers that is `1` is least significant bit. +This property lets us extract the least significant bit of `display_value` by using `display_value & 1`. + +For the next step, we use `>>`, the [right-shift operator][right-shift-operator]. +The expression `a >> b` shifts all of `a`'s bits to the right by `b` places, and returns the resulting number. + +For example, if we used the numbers `5` (`101` in binary) and `1`, we get `2` (`10` in binary): + +```python +0b101 >> 1 +#=> 0b010 +``` + +You can see how `& 1` and `>>= 1` perform the same function as the `% 2` and `//= 2` used in earier variants. + + +[bitwise-operators]: https://www.w3schools.com/programming/prog_operators_bitwise.php +[right-shift-operator]: https://www.geeksforgeeks.org/software-engineering/right-shift-operator-in-programming/ diff --git a/exercises/practice/eliuds-eggs/.approaches/parameter-modification/snippet.txt b/exercises/practice/eliuds-eggs/.approaches/parameter-modification/snippet.txt new file mode 100644 index 00000000000..c3d5eeb7bf4 --- /dev/null +++ b/exercises/practice/eliuds-eggs/.approaches/parameter-modification/snippet.txt @@ -0,0 +1,6 @@ +def egg_count(display_value): + eggs = 0 + while display_value: + eggs += display_value % 2 + display_value //= 2 + return eggs \ No newline at end of file