0

Let me explain what I'm trying to do. I have data like

const dataByState = {
    'Washington' : { ElectoralVotes : 12, RChance: 54, DChance: 46 },
    'Oregon': { ElectoralVotes: 7, RChance: 51, DChance: 49 }, 
     .
     .
     .
    'Hawaii' : { ElectoralVotes: 4, RChance : 40, DChance: 60 }
}; 

where one of the above key-value pairs like

'Hawaii' : { ElectoralVotes: 4, RChance : 40, DChance: 60 }

means "In the state Hawaii, which has 4 electoral votes, there is a 40% chance of the Republican Candidate winning and a 60% chance of the Democrat candidate winning". What I'm ultimately trying to do is calculate the chance of each candidate winning the election. How this would be done in a perfect world is

  1. Iterate through all 2^51 combinations of states
  2. For each combination c, its combined electoral votes are greater than or equal to 270, add it to a collection C of collecions of states
  3. For the Republican candidate, sum up the probabilities of winning each combination of states in C; call that value r. That's his/her chance of winning. The Democrat's chance is 1 - r.

But since I can't go through all 2^51, what I'm doing is choosing some N smaller than 51 and doing

  1. Find a random 2^N combinations of states whose combined electoral votes sum to greater than or equal to 270; call this combination C.
  2. For the Republican candidate, sum up the probabilities of winning each combination of states in C; call that value r. Multiply r by 2^(51-N). That's approximately his/her chance of winning. The Democrat's chance is 1 - r.

Anyhow, this doesn't seem to be working and I'm wondering whether my logic is wrong (I haven't taken statistics since college 3 years ago) or if I'm running into rounding errors. I'm getting a near 100% of the Republican winning (i.e. America being made great again) when I make the chance even in every state, which is wrong because it should calculate to about 50/50.

Code dump: https://jsfiddle.net/pqhnwek9/

user6048670
  • 2,861
  • 4
  • 16
  • 20

1 Answers1

0

The probability of a republican victory is

probRepVict = 0
for(combination in combinations) {
    if(combination is republican victory) {
        probRepVict += proability of combination
    }
}

As you observe it is not feasible to calculate the entire sum. Hence, you choose some subset C to try to estimate this probability.

N = number of combination // 2^51
n = size of C
probRepVictEstimate = 0
for(combination in C) {
    if(combination is republican victory) {
        probRepVictEstimate += proability of combination
    }
}

probRepVictEstimate *= N/n

In the last statement we assume that the probability of a victory scales linearly with the size of the subset.

I believe the logic goes wrong at several places in the script:

(1) When generating the random number you might not get a sufficiently many bits of randomness. For instance if there were 54 states you would be outside of the safe integer range. Some implementations might give you even less fewer bits of randomness (it did break for me in Node, which only give 32 bits). Thus I suggest adding a function

function getRandom() {
    // Generate 32 random bits                                                  
    var s = Math.floor(Math.random()*Math.pow(2, 32)).toString(2)
    return new Array(32 - s.length + 1).join("0") + s
}

Replacing

const rand = Math.floor(Math.random() * Math.pow(2,states.length));

with const rand = getRandom() + getRandom();, and replace getCombo with

const getCombo = (i) => {
    let combo = [];
    for(var j = 0; j < states.length; ++j)
        if(i[j] == "0")
            combo.push(states[j]);
    return combo;
}

(2) You need to count both wins and losses for the republican party to be able to estimate the probability. Thus you cannot add the complement of a combo (by the way, ~ is a bitwise operations, hence convert the operand to a 32-bit integer, so your code does not work as intended). Hence your code should be simplified to:

...
if(!winningCombos.hasOwnProperty(rand)) {
    const stateCombo = getCombo(rand);
    if(hasSufficientVotes(stateCombo))
    {
        winningCombos[rand] = stateCombo;
        ++wins;
    }
    ++count;
}
...

(3) You should scale repubChanceSum by N/n, where N = Math.pow(2, 51) and n = limit. Note that limit should be considerably greater than winningCombos.length.

With these modifications the code correctly predicts a ~50% probability. See this modified fiddle. Let's hope we get a more optimistic outlook for the future with more realistic probabilities.

YellowBird
  • 633
  • 3
  • 9