It might help to solve this in a more mathsy way. Imagine you are trying to come up with a line-equation for a line that graphs the score for any given index in your list of items. The x-axis would be your item indices, and the y-axis would be the corresponding scores.
Let's simplify the requirements initially - let's ignore the whole business of "the first twenty..." and "the next ten...". Let's just say the first score should be 1.0
, the second score should be 0.5
, the third 0.25
, etc. The pattern here is pretty simple. The score starts at 1.0
, and gets divided in half each single "step" we take.
The first few points that make up this line would be (0.0, 1.0)
, (1.0, 0.5)
, (2.0, 0.25)
and (3.0, 0.125)
.
Another way of writing this would be (0.0, 1.0 / 1)
, (1.0, 1.0 / 2)
, (2.0, 1.0 / 4)
and (3.0, 1.0 / 8)
. The pattern here is that, for each "step", the next y-value will be one over the next power of two (2**0==1
, 2**1==2
, 2**2==4
and 2**3==8
.)
The equation for this line would therefore be: y = 1.0 / (2 ** x)
:

Let's reintroduce one of the requirements: The score must be cut in half for every ten "steps".
Easy. Just make x
increase ten times more slowly: y = 1.0 / (2 ** (x // 10))
.
Note: //
means integer division, so that we're raising 2
to the power of an integer, not a floating-point number. This is equivalent to int(x / 10)
.
Final requirement: The first twenty items must have a score of 1.0
. Starting at item# 21, the score gets cut in half every ten "steps", starting at 0.5
.
You can think of this as a piece-wise function, where the function always yields 1.0
if x <= 20
, otherwise it yields the computed value from our line-equation. With one small difference: We need to subtract 1
from the exponent so that our pattern starts at 0.5
at item# 21 rather than 0.25
. This is because 2 ** (20 // 10) == 2 ** 2 == 4
, whereas 2 ** ((20 // 10) - 1) == 2 ** (2 - 1) == 2 ** 1 == 2
. Note: Item# 21 has an index of 20
, since the first item has an index of 0
.
In Python, you could define a function that takes an index as a parameter, and returns the corresponding score for that index:
items = ["a", "b", "c", "d", "e"] * 12 # Some items...
def get_index_score(index):
threshold = 20
return 1.0 if index < threshold else 1 / (2 ** ((index // 10) - 1))
total_score = sum(get_index_score(index) for index, _ in enumerate(items))
print(f"Total score for {len(items)} items: {total_score}")
for index, item in enumerate(items):
score = get_index_score(index)
print(f"[{index}]: {item} => {score}")
Output:
Total score for 60 items: 29.375
[0]: a => 1.0
[1]: b => 1.0
[2]: c => 1.0
[3]: d => 1.0
[4]: e => 1.0
[5]: a => 1.0
[6]: b => 1.0
[7]: c => 1.0
[8]: d => 1.0
[9]: e => 1.0
[10]: a => 1.0
[11]: b => 1.0
[12]: c => 1.0
[13]: d => 1.0
[14]: e => 1.0
[15]: a => 1.0
[16]: b => 1.0
[17]: c => 1.0
[18]: d => 1.0
[19]: e => 1.0
[20]: a => 0.5
[21]: b => 0.5
[22]: c => 0.5
[23]: d => 0.5
[24]: e => 0.5
[25]: a => 0.5
[26]: b => 0.5
[27]: c => 0.5
[28]: d => 0.5
[29]: e => 0.5
[30]: a => 0.25
[31]: b => 0.25
[32]: c => 0.25
[33]: d => 0.25
[34]: e => 0.25
[35]: a => 0.25
[36]: b => 0.25
[37]: c => 0.25
[38]: d => 0.25
[39]: e => 0.25
[40]: a => 0.125
[41]: b => 0.125
[42]: c => 0.125
[43]: d => 0.125
[44]: e => 0.125
[45]: a => 0.125
[46]: b => 0.125
[47]: c => 0.125
[48]: d => 0.125
[49]: e => 0.125
[50]: a => 0.0625
[51]: b => 0.0625
[52]: c => 0.0625
[53]: d => 0.0625
[54]: e => 0.0625
[55]: a => 0.0625
[56]: b => 0.0625
[57]: c => 0.0625
[58]: d => 0.0625
[59]: e => 0.0625