0

I'm working on a markov chain and have created a 2d hashmap that calculates the weighted probabilities. The output of this works fine.

I'm looking to find the best way to output the next value. The way I have it at the moment isn't working properly. Ent1.first is the incoming midiNote. Ent2.first is the potential outgoing value and Ent2.second is the weighted probability.

When midiNotecomes in, I need to look into the table and find the weighted probabilities and using rand() pick the next value. One problem is that I only need this to happen once, not for every time in the for loop. Here is a snippet of my table calculation for simplicity's sake, but if you'd like me to post the entire code let me know.

void getCountTable(int midiNote) {
    for(auto const &ent1: cdf) {
        midiNote = ent1.first;
        for (auto const &ent2: ent1.second) {
            //console out all resulting note transition weights
            //std::cout << "Note: " << ent1.first << std::endl <<"Next note: " << ent2.first <<std::endl << "Weight: " << ent2.second << std::endl << std::endl;

            //TRYING TO FIGURE HOW TO HANDLE THIS.  JUST WANT TO HAPPEN ONCE FOR EACH INCOMING VALUE

            //psuedo-random values between 0-1 
            float r = static_cast <float> (rand()) / static_cast<float> (RAND_MAX);

            //calculate next value 
            if (r < ent2.second) {
                int output = ent2.first;
                std::cout << ent1.first << " " << output << std::endl;   
            }
        }
    }
}
Louis Langholtz
  • 2,913
  • 3
  • 17
  • 40

2 Answers2

0

Currently you are creating a new random number each time around your inner loop and comparing it with the chance for that possibility. This will mean that sometimes one item will match (good), but sometimes you will get zero or two or more items matching (it is random).

One way to pick the just one possibility out of all the options is to generate one random number, then loop through the possibilities until the sum of all the possibilities so far is greater than the random number that you have generated.

Rearranging your code (untested)

    midiNote = ent1.first;
    //psuedo-random values between 0-1 
    float r = static_cast <float> (rand()) / static_cast<float> (RAND_MAX);
    float sum = 0;
    int output = 0; 

    for (auto const &ent2: ent1.second){
       sum += ent2.second;
       if (sum >= r) {
          output = ent2.first;
          std::cout << ent1.first << " " << output << std::endl; 
          break;
       } 
   }

This should work, although given the inexactness of floating point additions, it might be worth defaulting output to the last item of ent1.second if nothing is found (e.g. this might happen if sum ends up at 0.999, but r was 0.9999)

The Dark
  • 8,453
  • 1
  • 16
  • 19
0

I've found there needs to be a comparison between the map and the incoming midi value. Also changed the probabilities to the first entry in the second map and the resulting output as the second entry. Modified some and provided all below.

void nextNote(int midiNote){
float r = static_cast <float> (rand()) / static_cast<float> (RAND_MAX);
int output = 0;
float sum = 0;
for (auto const & ent1: cdf){
    if(ent1.first == midiNote){
        for (auto const & ent2: ent1.second){
            sum+= ent2.first;
            std::cout <<sum << std::endl;
            if(sum >= r){
                output = ent2.second;
                std::cout << output <<std::endl;
                break;
            } 
        }
    }

}


}