There are many threads talking about how frustrating it is to delete 100 materia. I agree.
I have been thinking on this for quite some time, and I wanted to come up with a solution. I am not 100% sold on it, as it would require 6 more bits of data for every piece of gear stored on the server. So, here's my idea:
Once the player passes the average number of mateira destroyed, the %chance of success goes up, linearly, until twice the average, ending at 100%. Here are a few examples of what this could look like:

6.67% and 16.67% included because I don't know if the 7/17% is rounded.
Pros:- It is deterministic. If we want to keep randomness involved, lets at least guarantee someone will get it, eventually
- You can't cheese this by intentionally sinking low-tier melds, as the %chance resets after successfully melding a materia. Sure you can gamble with the low-tier materia, but how many of the high tier melds would you realistically save? And since low-tier materia have a higher meld chance, you can't really save that many.
- It's not hard to communicate to players that the chance will increase after many materia have been lost, up to 100%, eventually.
- The algorithm is not complex, and simple enough for those more math-hardened players.
- It works with melding one at a time and bulk-melding.
Cons:- The algorithm gets a little wonky when the base chance is above 50%, but it works out just the same.
- Since the piece of gear needs to store (internally, probably) how many materia have been lost into it, we need 6 bits of data to store this. I don't know how their data model works. So this could be a deal-breaker. We need 6 bits because, at the moment, the lowest chance is 5%, and 39, the highest number of broken melds, requires 6 bits to represent.
Here's the algorithm in C# for those interested:
Code:
float newRate(int numBroken, float baseRate)
{
float newRate;
float meanNumMeld;
if (numBroken <= 0)
{
newRate = baseRate;
}
else
{
meanNumMeld = 1 / baseRate;
if (numBroken - meanNumMeld <= 0)
{
newRate = baseRate;
}
else
{
newRate = (numBroken - meanNumMeld + 1) * baseRate;
if (newRate > 1)
{
newRate = 1;
}
}
}
return newRate;
}