1) The naive solution: The naive solution to this problem might be to assume that you "average" m*a/2 per hit, and therefore should take about (HP/(ma/2)) hits to kill the monster. For starters, this is wrong, and secondly, it's REALLY wrong.
Example 1) 70% accuracy, max hit of 40. Monster has 5 hp.
Naive solution: 5 / (40*.7/2) = 5/14 hits. This should take you less than 1 hit to kill the monster. Clearly wrong.
Lord Klotski's calculator: 1.57688. This is far more reasonable; you might miss once first, you might miss twice, you might hit a 1 or a 2, etc.
Well, perhaps the naive solution is a good approximation to larger values?
Example 2) 90% accuracy, max hit of 40. Monster has 125 hp.
Naive solution: 125/(40/2*.9) = 125/18 = 6.94444
Lord Klotski's Calculator: 7.47933
So our naive solution is off by .5, or about 9%. Nawt gud.
Why does the naive solution not work? Because, to be brief, it handles fast KOs very badly. I could post a full worked example, if needed.
2) Simulated statistical samples: The idea of doing several statistical samples came to mind when I designed this. The computer could generate a series of "hits" that would simulate killing the monster over and over, then I could take the mean of that and hope it was accurate.
The issue with this setup is that it is REALLY inaccurate for HP > 2-3 times the max hit (and gets worse if you want to do a GWD monster lol, or the CB), and requires a TON of iterations to get anywhere reasonably close to the "true mean". I happen to know that the variation of a single hit is random in both m^2 and a^2, so the variance is ugly high. I'd need to be running, for my higher HP values, hundreds of thousands of kills, each taking several hits...nawt a good idea. Furthermore, I'm not guaranteed that it's accurate.
3) Naive Recursion: This is the first of the two truly accurate ways to compute the expected value. Basically, we say that for a general hit, we will take off 0-max HP. Then , we define the expected number of hits to be 1+E(current HP - damage). In other words, the function refers to itself.
A quick example of recursion: The fibonacci series is defined recursively, where f(n) = f(n-1) + f(n-2). Try: f(3) = f(2) + f(1). We don't know f(2), so f(2) = f(1) + f(0). f(1) and f(0) are defined to be 1. Then we have f(3) = 2 + f(1). f(1) is defined to be 1, so f(3) = 2+1 = 3.
Now, if we try to compute f(5), we need f(4) and f(3). f(4) = f(3) + f(2). We need both of those: f(3) = f(2) + f(1). f(2) = f(1) + f(0), then we go back, calculating another branch of f(2), and then f(3) again. Finally we add these together to get f(5).
This method would have you, say for a max hit of 40, computing 40 different things each step/HP point (in addition to calling itself at it's own value if you miss). In other words, for a max hit of 40, and a HP of 120, you would require about 40^120 iterations. On a very good scientific calculator, you can find this equal to about 10^192 iterations. This program would not run in your lifetime. Sorry.
4) Recursion with Dynamic Programming (DP): - This is the algorithm that I used. Basically, I used some hax methods to not do the 40^120 iterations, and instead, should run so fast that you won't even notice that there's lag (unless you use the CB HP, which is 2000; it'll take 1-2 seconds or so). It has a couple creative ways of dealing with some of the fundamental issues with the recursive algorithm, such as the method does call itself with the same parameters (a well defined infinite loop, but you don't need to worry too much about that. I was also pretty tricky in a couple other places in the program, but see my previous statement.
The source code for the DP method is, as I said, available upon request. It took me four hours to write. Debugging recursion sucks.