0

I'm looking for a simple algorithm that reliably chooses the same 'winner' from 2 or more different numbers. There's no guarantees about the order in which you receive the numbers (but they can be sorted or something similar). An easy way of solving this would be picking the largest/smallest number, however this is not a very fair algorithm as it heavily favors large/small numbers.

The algorithm does not have to be perfect and may perform bad with few limited numbers, I thought of calculating hashes but this takes too much cpu time and want to stick to simple additions/multiplications/mod, it should remain simple.

follow up question is if there's a class of algorithms dedicated to this subject?

Thanks in advance!

  • I'm surprised to hear that your hash function is taking up so much CPU time. Which hash function are you using? Also, can you provide some more details here? Is it okay if there's one number that always wins? Is it a problem if someone observes which numbers win and starts changing their behavior in response? – templatetypedef Apr 14 '20 at 00:10
  • I am trying to program some small radio chips to communicate with each other using little CPU and memory, the end goal would be that they can resolve who gets certain time slots to send packets in (depending on mac address ect). Hence the same chip should 'win' every time. This needs to be performant enough and i'd rather not have a hashing algorithm eating way too many ms for the problem at hand. It's fine if there's certain numbers that nearly always win but i wonder if there was something better than just taking the one with the tiniest address that is also quite performant – Roel Moeyersoons Apr 14 '20 at 10:10
  • I'd use a linear-congruent random number generator -- so for a single multiply you get a "rank" for each number. (With a good LC, it should be screamingly unlikely to give the same rank for different numbers. But to tie break... xor the "rank" with each number and chose the larger -- this works if more than two numbers give the same "rank" !!) – Chris Hall Apr 14 '20 at 10:14
  • I'm getting a sinking feeling that your CPU may not be good at multiplying... A hardware assisted CRC ? Or, for mac addresses, maybe just XOR the bytes together to give an extra, first byte to compare -- then compare the actual mac addresses xor'd with that (since the leading bytes of the mac may not be very random) ? – Chris Hall Apr 14 '20 at 11:08
  • Both ideas should work, i ended up applying the first and that seems to work well. Thanks! – Roel Moeyersoons Apr 14 '20 at 17:42
  • Happy to hear that it works well :-) For completeness, I expanded a little by way of an answer. – Chris Hall Apr 15 '20 at 14:33

1 Answers1

0

Expanding comments to an answer...

I think what you want is a rough and ready "hash", which you can generate at low cost.

The following suggestions are designed to compare two "number"s to give an entirely deterministic, but not obvious ordering. To do that this extracts an n-bit "rank" for each "number" to be compared, then compares the "rank"s. If two "number"s have the same rank, then need to break the tie by comparing the "numbers" directly.


Using Multiplication

If your CPU has a fast n-bit multiply, then a good Linear Congruent pseudo-random number generator (with modulus 2^n) (LCG) will give a nicely random looking "rank" for each "number", at the cost of a single multiply.

The process is, then:

  1. "munge" each "number" to an n-bits

    If the "number" is more than n-bits long (you mention mac-address) then the first step is to munge that into n-bits. Start with "rank" set to something-large-and-random-looking (as many digits of pi, or e, say, as will fit). Then xor in the "number" n-bits at a time, rotating the "munge" between xors (at least 1 bit, but any odd number of bits, ceiling(n/3), perhaps). The rotation retains a little flavour of the order in which the n-bit parts are munged in.

    If the "number" is n-bits or less, starting with "munge" xor something-large-and-random-looking adds a little spice.

  2. Construct the "rank" from each "munge".

    Multiply the "munge" by a good n-bit LCG multiplier, to give the n-bit "rank".

  3. Compare and tie break.

    Compare the "rank"s, if they are not equal the process is complete.

    With a good LCG it's probably unlikely that two "numbers" will give the same "rank" (unless you've only got a 16-bit multiply).

    But in any case, if two "number"s do give the same "rank", then to break the tie you can fall back on comparing the numbers. To make that less obvious, you can xor each "number" with the "rank" before doing the comparison. This will work if more than two "number"s have the same "rank".

    [Remember that the ls-bits of an LCG are less-random-looking than the ms-bits. Since this is only a tie-break that probably doesn't matter, but if it is natural to compare the "number"s byte-wise (for example) it would be better to use the ms-bits of the "rank".]


Using only XOR and Rotate

If multiplication is too slow, which these days is still true for (small) micro-controllers, then let us assume the "number" must be processed 8- or 16-bits at a time.

Starting with (say) "munge"=157 or "munge"=31415, step (1) above will not do a bad job, particularly if the "number" is three or or more n-bit units.

A final stir: setting "rank" to "munge" xor ("munge" rotated by ceiling(n/3)), won't hurt.

Then proceed as (3), above.


Using CRC

If the CPU has a (fast) hardware assisted n-bit CRC generator, then either:

  • set "rank" to the CRC of "number"

or:

  • construct "munge" as above, and take CRC of that.

Then proceed as (3) above.

Chris Hall
  • 1,707
  • 1
  • 4
  • 14