Fluent-Random-Picker icon indicating copy to clipboard operation
Fluent-Random-Picker copied to clipboard

Weights using long

Open phillip-haydon opened this issue 2 years ago • 3 comments

Hey,

Is it possible to extend weights to support long, lookg like at the moment they only support int?

❤️

phillip-haydon avatar Sep 26 '22 17:09 phillip-haydon

Hey,

I limited the weights to int in the beginning, because Fluent Random Picker uses algorithms that sum up all the weights internally.

If the int weights are Int32.MaxValue and Int32.MaxValue, the sum can't be stored as an int. So I use long for the sum internally at the moment.

If I'd allow long weights and if they are Int64.MaxValue and Int64.MaxValue, the sum can't be stored as a long. I'd need a larger data type then internally...


2 ways are probably possible on my site:

  1. Replacing the algorithm that sums up the numbers (at least when the weights are longs) with one that works differently.
  2. .Net7 (will be available in about a month) offers a new Data type (Int128), which I could use to calculate a sum of longs. But this would only work in projects that are targeting .Net7 or higher.

Which .Net version are you targeting?

I'll take a closer look at this in the future.

ndsvw avatar Sep 29 '22 15:09 ndsvw

Hey,

Makes sense, you /could/ use BigInteger to sum the values. Tho it might be slow.

I'm currently targeting .net 7 rc1.

Right now I'm just casting to int so I'm not hindered, but what I'm using it for is figuring out in my game 'who' should hit.

Your library just makes mundane stuff easier :D

        var entity = Out.Of()
                         .Value(EntityType.Player).WithWeight((int)playerSpeed)
                         .AndValue(EntityType.Monster).WithWeight((int)monsterSpeed)
                         .PickOne();

phillip-haydon avatar Sep 29 '22 16:09 phillip-haydon

Notes for me:

  • SortingBasedWeightedLeftShuffle would break with longs because all results are 1 at some point.
Math.Pow(0.9999, 1/(double)int.MaxValue) // = 0.99999999999995348165526820594095624983310699462891
Math.Pow(0.9999, 1/(double)long.MaxValue) // = 1.00000000000000000000000000000000000000000000000000
Math.Pow(0.9999, 1/((double)long.MaxValue/10)) // = 1.00000000000000000000000000000000000000000000000000
Math.Pow(0.9999, 1/((double)long.MaxValue/1_000_000)) // = 1.00000000000000000000000000000000000000000000000000
Math.Pow(0.9999, 1/((double)long.MaxValue/10_000_000)); // = 0.99999999999999988897769753748434595763683319091797

So, maybe, there needs to be a fallback like Math.Pow(0.9999, 10_000_000/(double)n); for larger numbers. Maybe, a fallback should already be there for ints, because for ~0,000001% of the weights (higher if we would allow long weights), it does not even make a difference, whether they are int.MaxValue or int.MaxValue / 10 (edge case):

Math.Pow(0.99999999, 1/(double)int.MaxValue) // = 1.00000000000000000000000000000000000000000000000000
Math.Pow(0.99999999, 1/((double)int.MaxValue / 10)) // = 1.00000000000000000000000000000000000000000000000000

BTW: Random.NextDouble being exactly 0 could also be problematic as the result is always 0...

  • DefaultPicker would break because it calculates the sum resulting in a possible overflow (solvable with Int128 from .Net7 or BigInteger)
  • Everything else should still work with longs.

ndsvw avatar Oct 01 '22 07:10 ndsvw