So, I spent some time last week to work this problem out from first principles. It turns out that both expressions are wrong, but for different reasons. Here's the derivation; afterwards I'll explain where kahalm's version of the deriviation went wrong.
There are two ways to interpret the effect of parry-haste. One is to look at a fixed number of swings N. From this point of view, each parry-hasted attack reduces the total time T it takes to reach N swings. In other words, we get N swings in T-dT time, where dT is the total amount of time "saved" via parry-hasting. We can compute the parry-hasted swing rate as:
R = N/(T-dT)
And of course, our swing timer would be the inverse of this, 1/R = (T-dT)/N.
The other way to interpret the effect of parry-haste is to look at a fixed amount of time T. In this formulation, each parry event gives us an additional "fractional swing," because now we're producing swings slightly faster than usual. In other words, we produce N+dN swings in time T, where dN is the sum of all the fractional swings we get from parry-hasting. The swing rate is then calculated as:
R = (N+dN)/T,
with the swing timer again being the inverse of this.
It's important to note that in the long-time limit (T->infinity), (N+dN)/T must approach the same value as N/(T-dT), since they're both perfectly valid ways of calculating the average rate. It was while working through the algebra for these two approaches that I discovered what was happening.
We're going to derive this for a generic system of two events A and B. The problem definition is very simple:
We have events A and B.
Event A happens with frequency Rao (in vacuo)
Event B happens with frequency Rbo (in vacuo).
Event B has a Pb percent chance to decrease the time until the next A by 0.24/Rao.
Event A has a Pa percent chance to decrease the time until the next B by 0.24/Rbo.
Given this well-defined problem, what is the correct analytical answer, or at least the correct set of coupled equations we need to be solving?
Event A will be player auto-attacks, and Event B will be boss auto-attacks. For weapon speed Wao, a player makes N auto-attacks in time T before parry-haste is considered. Thus,
Code:
Rao = N / T = 1 / Wao.
The actual rate of player auto-attacks after parry-haste will be Ra.
In a similar fashion, Wbo is the base boss weapon speed Rbo is the base boss attack rate, and Rb is the rate after parry-haste.
(Note: The nomenclature here is partly blizzard's fault. They call it "weapon speed," but then give a value in seconds. Anyone with a basic science background knows that a speed is a rate, and should be in units of 1/s. Nonetheless, I'm going to stick with their convention of calling Wao and Wbo "speeds" but using units of seconds. Hopefully this isn't too confusing, since I'll be attempting to use Rao and Rbo rather than Wao or Wbo wherever possible.)
We'll now calculate the average Ra with each approach to make sure they agree:
In time period T, we'd have T.*Pb.*Rb parries. Each parry reduces the swing timer by 0.24*Wao seconds. We're sure this is correct, because on a successful parry the swing timer is reduced by 40% of the pre-parry-haste value Wao, not the post-parry-haste value Wa. In other words, whether or not your previous attack was parry-hasted has no effect on the reduction (in seconds) you get during your next swing. This is consistent with the old EJ parryhaste mechanics thread, and there will be further proof later in the derivation that using 0.24*Wa gives you an incorrect answer.
This leads to a cumulative time reduction dT:
Code:
dT = 0.24*Wao*T*Pb*Rb
Just to check that this makes sense, this is:
Code:
dT = (swing timer redux per parry)*(total time T)*(Boss Parry Chance)*(Boss Attack Rate)
dT = (swing timer redux per parry) * (total number of parries in time T)
I'm going to define a constant c = 0.24*Pb to simplify the rest of the derivation, such that
First Approach:
We have N attacks in time (T-dT), for a rate of:
Code:
Ra = N / (T-dT) = (T*Rao)/(T - c*T*Rb*Wao) = Rao/(1-c*Rb*Wao)
Ra*(1-c*Rb*Wao) = Rao
Ra = Rao + c*Rb*Ra/Rao
Note that this differs from both the version we use in the MATLAB (which is Ra = Rao + c*Rb*Rao/Ra given that pph=Wao/Wa=Ra/Rao) as well as kahalm's version (Ra = Rao + c*Rb). I'll come back to that point in a second.
Second Approach:
The other approach is to say that in time T, we actually get N + dN attacks due to parry haste. N is still T*Rao, but since it only takes (T - dT) to the Nth attack, there's some time left over (dT) during which we can sneak in dN extra attacks. The average rate is then Ra=(N + dN)/T.
This is a crucial juncture, because there's a "wrong way" and a "right way" to calculate dN. I will now demonstrate that by doing it the "wrong way" you get kahalm's answer, and by doing it the "right way" you get an answer consistent with the previous N/(T-dT) formulation that you suggested.
Wrong Way: In time dT, you get dN = dT/Wao = dT*Rao extra attacks. Then
Code:
Ra = (N + dN)/T = (T/Wao + dT/Wao)/T
= (T*Rao + c*T*Rb*Wao/Wao)/T = Rao + c*Rb
Ra = Rao + c*Rb
This is exactly the form that kahlam's equations take.
Right Way: In time dT, you actually get dN = dT/Wa = dT*Ra extra attacks. The justification is very straightforward: There's nothing "special" about that period dT that would prevent us from seeing parry-hasting. Since dN represents the total number of attacks we generate in dT (including the effects of parry-haste), we have to use the rate Ra. This is no different from saying that over a 5-minute fight, we'd expect 5*60*Ra parry-able attacks. Thus:
Code:
Ra = (N + dN)/T = (T/Wao + c*T*Rb*Wao/Wa)/T
= Rao + c*Rb*Ra/Rao
which agrees with the N/(T-dT) formulation.
I'll note here that if you make an error calculating dT, specifically by using Wa instead of Wao, the N/(T-dT) formulation also gives you kahlam's answer, Ra = Rao + c*Rb. This is the proof I mentioned up when I defined dT. If you use Wa to define dT, the two averages don't come out in agreement, which they must if they are to have any physical meaning.
To summarize, here are the three possible forms of Ra that we have:
Code:
Ra = Rao + c*Rb*Ra/Rao (derived from first principles two different ways)
Ra = Rao + c*Rb (kahlam's - caused by an error in the above derivation)
Ra = Rao + c*Rb*Rao/Ra (MATLAB)
The version I've derived here from first principles is correct.
Kahlam's, while wrong, is the closest to correct. It's error is simply a factor of (Rao/Ra) in the coupling term, and it's arrived at by making a minor mistake in the derivation. It's also worth noting here that this is effectively a first-order approximation, since he's substituted Wao in a spot where Wa should have been.
Note that the result of this is to slightly over-couple the equation, since (Rao/Ra > 1)
The MATLAB version is the farthest off. It differs by a factor of (Rao/Ra)^2, thus over-coupling more than kahlam's version. Furthermore, I haven't found any sort of rational justification for this factor. The closest I can come is by making two consecutive mistakes in the derivation: first by calculating dT as c*T*Rb*Wa, followed by calculating dN as dT/Wao. Those two errors are mutually inconsistent, so there's no chance that this is actually a lucky string of errors that corrects themselves. My best guess is that when we were fooling around with the parry-haste code, I either made a typo or we were trying to do something unnecessarily complicated to account for higher-order parry effects (i.e. two parries within one swing timer).
One can perform a similar calculation for Rb to get the complete system of equations that need to be solved. They are:
Code:
Ra = Rao + c1*Rb*(Ra/Rao)
Rb = Rbo + c2*Raa*(Rb/Rbo)
Raa = f(Ra) = A + B*Ra
where A is the static term representing attacks that are independent of parry haste (33/6 for warriors, for example) and B is the contribution from attacks that benefit from parry-haste (1 for warriors).
As a final thought, it should be noted that these equations are not as simple to solve as the ones kahalm came up, but they are solvable. Here's the derivation:
Code:
Starting from:
Ra = Rao + c1*Rb*Ra/Rao
Rb = Rbo + c2*Raa*Rb/Rbo
Raa = A + B*Ra
Solve Rb eqn for Rb:
Rb(1-c2*Raa/Rbo)=Rbo
Rb = Rbo/(1-c2*Raa/Rbo)
Plug into Ra eqn:
Ra - Rao = (c1*Ra/Rao)*Rbo/(1-c2*Raa/Rbo) = (c1*Ra/Rao)*Rbo^2/(Rbo - c2*Raa)
(Ra - Rao)*(Rbo - c2*Raa) = c1*Ra*Rbo^2/Rao
Ra*Rbo - c2*Ra*Raa - Rao*Rbo + c2*Raa*Rao - c1*Ra*Rbo^2/Rao = 0
-Ra*Rbo + c2*Ra*(A+B*Ra) + Rao*Rbo - c2*Rao*(A+B*Ra) - c1*Ra*Rbo^2/Rao = 0
c2*B*Ra^2 + (c2*A + c1*Rbo^2/Rao - Rbo - c2*B*Rao)*Ra + (Rao*Rbo-c2*A*Rao) = 0
let:
a=c2*B
b=(c2*A + c1*Rbo^2/Rao - Rbo - c2*B*Rao)
c=Rao*Rbo - c2*A*Rao
and we have a standard quadratic equation:
a*Ra^2 + b*Ra + c = 0
The solution is then
Ra=(-b - sqrt(b^2-4*a*c))/(2*a)
where I've chosen the negative root of the square since it gives a realistic result
(the positive root gives us something ridiculous like 13 attacks per second).
Some quick testing shows that my iterative technique gives me the same result as the analytical solution out to 8 decimal places.
I've already updated my local version of the code to reflect this model. I'm in the process of nailing down the particular values of A and B for each class (and possibly another constant C for DK's courtesy of the way Rune Strike interacts with boss attacks). Once that's done, I should easily be able to calculate efficiency values for each of the four classes, and thus get around to completing the article.
Bookmarks