the range of getRandom() is 0 <= getRandom() < 1
first is getRandom() < 1, which is always true
second is getRandom() < 1/2, which is 50% chance of swapping - 50% chance 0 is chosen, 50% chance 1 is chosen
third is getRandom() < 1/3, which is 33% chance of swapping - 33% is a 1/6th (1/3 * 1/2) total of each of the other two, 1/2(3/6) - 1/6 is 2/6, so each is now 33%
fourth is getRandom() < 1/4, which is 25% chance of swapping - 25% is a 1/12th (1/4 * 1/3) total of each of the other three, 1/3(4/12) - 1/12 is 3/12, so each is now 25%
looks like a VERY even distribution to me, if you see a flaw, let me know
it doesn't have a guaranteed winner, you're right
if targets is 0, it won't do anything in the for loop
but then again is there really a winner if there is nothing to select from?
I'd say that not setting %final is proper behavior when there is no selection, but it's undefined behavior so if it crashes/BSoDs/deletes blockland/wins the lottery, I'd say that's completely ok too