Best approach to make a reverse five-star calculator
<p>In the 5-star rating system, I have a known number of ratings.<br>
I have the final (weighted) average of all those N ratings, let's say it is R (float to two decimal places).<br>
I would like to write a python script which generates all possible combinations (Total of weighted averages) and print out only the one(s) which result in R. </p>

<p>How would I approach this problem?</p>

<p><strong>Example:</strong></p>

<p>N= 20 customers rated a product<br>
R = 3.85 is the average rating</p>

<p>Output I am looking for:</p>

<blockquote>
  <p>10 five-stars, 4 four-stars, 1 three-stars, 3 two-stars, and 2 one-star </p>
</blockquote>

<p>is one combination which would give 3.85 by 20 customers.</p>

<p>How would I test if there are any more combinations which could result in the given R and print them out as well? </p>

<p>You can enumerate all the distributions of votes using a recursive algorithm and then check which of those have correct weighted average. But note that the number of combinations grows quickly.</p>

<pre><code>def distributions(ratings, remaining):
    if len(ratings) &gt; 1:
        # more than one rating: take some for first and distribute rest
        for n in range(remaining+1):
            for dist in distributions(ratings[1:], remaining - n):
                yield [(ratings[0], n)] + dist
    elif len(ratings) == 1:
        # only one rating left -&gt; all remaining go there
        yield [(ratings[0], remaining)]
    else:
        # should never happen
        raise ValueError("No more ratings left")

def weighted_avg(votes):
    return sum(r * n for r, n in votes) / sum(n for r, n in votes)

for dist in distributions([1, 2, 3, 4, 5], 20):
    if weighted_avg(dist) == 3.85:
        print(dist)
</code></pre>

<p>In total there are <code>10626</code> distributions, and <code>146</code> of those yield the correct average. Output (some):</p>

<pre><code>[(1, 0), (2, 0), (3, 3), (4, 17), (5, 0)]
[(1, 0), (2, 0), (3, 4), (4, 15), (5, 1)]
...
[(1, 2), (2, 3), (3, 1), (4, 4), (5, 10)]
...
[(1, 5), (2, 0), (3, 1), (4, 1), (5, 13)]
[(1, 5), (2, 1), (3, 0), (4, 0), (5, 14)]
</code></pre>

python algorithm