Toss Strange Coins

Updated on 01 July, 2025
Toss Strange Coins header image

Problem Statement

In this problem, you're provided with an array of probabilities called prob, where each element prob[i] represents the probability of the i-th coin showing heads when it is tossed. The task is to compute and return the total probability that exactly target number of coins show heads when all coins are tossed exactly once.

The complexity and challenge lie in calculating the exact probability distribution derived from multiple independent probability events (coin tosses), each with potentially different outcomes.

Examples

Example 1

Input:

prob = [0.4], target = 1

Output:

0.40000

Example 2

Input:

prob = [0.5,0.5,0.5,0.5,0.5], target = 0

Output:

0.03125

Constraints

  • 1 <= prob.length <= 1000
  • 0 <= prob[i] <= 1
  • 0 <= target``<= prob.length
  • Answers will be accepted as correct if they are within 10^-5 of the correct answer.

Approach and Intuition

To solve this problem, the logical approach involves using dynamic programming, particularly because we need to keep track of varying probabilities across multiple coin tosses.

  1. Define a 2D array dp where dp[i][j] holds the probability of getting exactly j heads out of the first i coins.
  2. Initialize the DP table:
    • dp[0][0] is initialized to 1 since the probability of getting 0 heads out of 0 coins is certain (100%).
    • For all j > 0, dp[0][j] should be 0 because it's impossible to get more than 0 heads from 0 coins.
  3. Start filling the table:
    • For each coin i, update dp[i][j] considering two cases:
      • The coin shows tails, and the number of heads we count come solely from the first i-1 coins.
      • The coin shows heads, thus adding one to the count of heads from the first i-1 coins.
    • This translates into the recurrence relation:
      • dp[i][j] = dp[i-1][j] * (1 - prob[i-1]) if the i-th coin is tails.
      • dp[i][j] += dp[i-1][j-1] * prob[i-1] if the i-th coin is heads.
  4. The desired probability is found at dp[n][target] where n is the number of coins.

This approach carefully considers each coin's individual probability and aggregate results leading up to the target, ensuring a comprehensive calculation framework. The constraints allow this method to work efficiently, as dynamic programming helps in avoiding redundant calculations and excessive recursive depth which could arise in a more naïve approach.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    double headProbability(vector<double>& probabilities, int desired_heads) {
        int total_flips = probabilities.size();
        vector<double> dp(desired_heads + 1, 0.0);
        dp[0] = 1.0;
    
        for (int current_flip = 1; current_flip <= total_flips; current_flip++) {
            for (int heads_count = desired_heads; heads_count >= 1; heads_count--) {
                dp[heads_count] = dp[heads_count - 1] * probabilities[current_flip - 1] + dp[heads_count] * (1 - probabilities[current_flip - 1]);
            }
            dp[0] *= (1 - probabilities[current_flip - 1]);
        }
    
        return dp[desired_heads];
    }
};

The code provided represents a solution to calculate the probability of getting a specific number of heads when tossing coins, where each coin has its own unique probability of landing heads. Written in C++, the class named Solution includes a method headProbability which performs this calculation following these steps:

  1. Retrieve the total number of coin flips which is determined by the size of the probabilities vector.
  2. Initialize a dynamic programming array dp with a size one greater than the desired number of heads (desired_heads + 1). This array is used to store probabilities of achieving head counts up to desired_heads. Set the first element (dp[0]) to 1.0, representing the probability of zero heads.
  3. Iterate over each coin (from current_flip = 1 to total_flips). For each iteration, update the dp array:
    • Adjust the probabilities for obtaining from 1 up to desired_heads heads using the current coin's probability to land heads. This update considers both outcomes: getting an additional head with this flip and not getting a head.
    • Update the probability of obtaining zero heads by considering the current flip results in tails.
  4. At the completion of the iterations across all coins, the value at dp[desired_heads] will indicate the probability of achieving exactly the desired_heads count.

This implementation effectively utilizes dynamic programming to reduce computational complexity, optimizing the process of calculating probabilities across multiple conditional events (coin flips). Each update is based on the outcomes of the previous flips, which ensures that all possible scenarios are considered without redundant calculations.

java
class Solution {
    public double calculateProbability(double[] probability, int requiredHeads) {
        int totalFlips = probability.length;
        double[] dpCache = new double[requiredHeads + 1];
        dpCache[0] = 1.0;
    
        for (int index = 1; index <= totalFlips; index++) {
            for (int headCount = requiredHeads; headCount >= 1; headCount--) {
                dpCache[headCount] = dpCache[headCount - 1] * probability[index - 1] + dpCache[headCount] * (1 - probability[index - 1]);
            }
            dpCache[0] *= (1 - probability[index - 1]);
        }
    
        return dpCache[requiredHeads];
    }
}

This Java solution is designed to solve the problem of calculating the probability of getting a specific number of heads when flipping coins with varying probabilities of landing heads. The core of the solution utilizes dynamic programming to build up probabilities incrementally.

  • Initialize an array dpCache to store the probabilities of achieving a given number of heads up to and including requiredHeads. Set the probability of achieving zero heads (all tails) to 1.0 initially because this is the only achievable outcome without any flips.

  • Progress through each coin's probability using an outer loop, while the inner loop calculates the probability of achieving every possible number of heads from 1 up to requiredHeads based on the outcomes (heads or tails) of the current coin.

  • For each number of headCount from requiredHeads decrementing to 1:

    • Calculate the new probability of achieving headCount heads by considering both possible outcomes from flipping the current coin:
      • Getting an additional head from the current flip, in which you take the probability of one head less from the previous results, and multiply it by the probability of the current coin landing heads.
      • Not getting a head on the current flip, hence take the probability from the same number of heads from the previous results but multiplied by the probability of the coin landing tails.
  • Update the probability of getting zero heads by multiplying the current value by the probability that the current coin lands tails.

  • After iterating through all coins, the required result, which is the probability of getting exactly requiredHeads heads, can be found in dpCache[requiredHeads].

This method efficiently calculates the exact probability by programmatically combining prior results and the outcomes specific to each coin. The ability to maintain a running total and adaptively compute each outcome is the strength of this dynamic programming approach. By the end of this process, obtain the probability of achieving the specified number of heads given the unique probability of each coin flip.

python
class Solution(object):
    def coinTossProbability(self, probabilities, k):
        length = len(probabilities)
        dp_array = [0] * (k + 1)
        dp_array[0] = 1
            
        for index in range(1, length + 1):
            for j in range(k, 0, -1):
                dp_array[j] = dp_array[j - 1] * probabilities[index - 1] + dp_array[j] * (1 - probabilities[index - 1])
            dp_array[0] *= (1 - probabilities[index - 1])
    
        return dp_array[k]

The given problem, "Toss Strange Coins," focuses on computing the probability of landing exactly k heads, given a list of probabilities for each coin's head either showing up or not. This Python solution capitalizes on dynamic programming to tackle the problem efficiently.

The solution initiates by defining the coinTossProbability method within the Solution class, where it accepts probabilities (a list where each element corresponds to the probability of a specific coin landing heads) and k (the exact number of heads required). First, you establish a dp_array array with a size of k + 1 initialized to zero, but setting dp_array[0] = 1 to account for the scenario where no heads are required.

As you iterate through each coin's probability, you adjust the dp_array to reflect the current state's probability by moving backwards from k to 1 in the nested loop. This ensures that the computation for probability of j heads considers only the outcomes up to that coin, preventing overwriting of required previous calculations. Notably, for each coin, the zero-head scenario is updated outside the inner loop.

  • The formula employed reflects a fundamental probability principle:
    • When adding the current coin to the tosses, the probability of getting j heads is derived from either:
      • The scenario where the previous toss resulted in j-1 heads and the current coin adds one more head.
      • Or the scenario where the previous toss already had j heads, and the current coin does not contribute another head.
  • This culminates in filling the dp_array with probabilities of having 0 to k heads given all coins.

Finally, the result dp_array[k] is returned, which gives the probability of obtaining exactly k heads with the given coin probabilities using dynamic programming efficiently and effectively.

Comments

No comments yet.