Need a fast von Neumann randomness extractor
What would be the most efficient way of coding this:
https://en.wikipedia.org/wiki/Bernou...ess_extraction Code:
input output The problem is that this maybe very inefficient. I mean how do I work with two bits at a time. I don't even think I can do this in C efficiently. Any hints on how to go about this ? I need it because I want to extract randomness from radio signals, and although hashing the input works well, when I look at the histogram I see that it is not level and that there are many values at the two extremes, maybe due to non-uniformity. I was thinking this extractor would help ... I'm no expert tho, but I will try it. One thing I have done is use GIMP on a generated pnm and tried to change the histogram by hand. It has worked quite well, but would like to do it a different way if possible. EDIT: The idea is from: http://www.entropykey.co.uk/res/down...xplanation.pdf Basically the von Neumann extractor first, and then the hash (which I have working already). |
Hmm, a quick thought...
I'd use Perl to get around it, use a for loop, but step per two, then get bit (n) and bit (n-1) and compare. A quick table: Quote:
Thor |
Ok, I think this code works, but do check it:
PHP Code:
EDIT: I've done more testing and it works very well, especially after hashing. So I do raw -> extractor -> hash, and the output is very much random. However, I do want someone to look over the code and see if it does what I think it does. I've never coded stuff like this before. |
I wrote a slightly optimized version. It uses a 256-byte translation table (small enough to stay in CPU cache) to map each input byte to 0 to 4 output bits. Using a single core on Athlon II X4 640 (3GHz) it translates about 200 MB/s (1600 Mbit/s). (Tested using a 256MB file containing all byte values in sequence.)
This is not the fastest I can do, but going faster gets less portable and progressively harder to read and to maintain. Depending on how you use the data, you probably want to tweak the input and output buffer sizes. Code:
#include <unistd.h> Code:
unsigned char *in_next = in_data; The bitmask array maps each input byte into two nibbles (high and low four bits):(the number of) bits and part. I used a trivial generator program to compute the array. (Note: And I got it wrong on the first edit. This is the fixed version.) When a full byte is extracted, we need to extract the high bits (since they are the oldest bits), and keep the excess (low/new) bits intact. Masking the input (the last line) is not really necessary, since we do shift the value before adding the new bits (i.e. new bits are always added to clear bits) -- in other words the results are the same if you remove the masking altogether -- but I like to be careful. The program will work with both blocking and non-blocking standard input and output. Let me know if you want it in a "library" form you can embed in your own code. I'll take a look at your own code next, H_TeXMeX_H. Edit: H_TeXMeX_H, your code should work perfectly, as far as I can tell. I can see nothing wrong in it. (I did not test it, though.) Your code will be limited by stdio.h (fread() and fwrite()) speed; that is why I used low-level I/O instead. You can alleviate that by reading and writing larger buffers at once, or you too can switch to low-level I/O. Also, my tests indicate that on an Athlon II X4 640, doing bit pairs at a time takes about 5x to 6x the time the lookup takes, thus incurring > 5x slowdown compared to my lookup method. Note: This is the fixed version, which only works with architectures that have 8-bit unsigned chars; the code does not check that, though. Look at my post #10 in this thread for a better example code. It is slightly faster, too, and much more portable. |
Wow, that's way past my abilities. Many thanks for taking the time to write it. I will try it right away :)
|
Ok, I have fixed my code and it works properly now, there was a mistake which I missed:
PHP Code:
However, there is something unusual... @ Nominal Animal Using your code I don't get the same result. This is strange. I tested results from both and mine produces more random output. I'm not sure of the cause. Yours is most certainly the fastest. I will try to investigate into the cause of this discrepancy. |
Quote:
Edit: Oh, yes, I mangled the output bit order. Fixed now; both my codes yield the same output, the one I outline in post #9 in this thread. |
Oh, ok, the output order may be it. I'll run some more tests.
|
Yes, there was a bug on my code (in the bitmask array and how it was used). It's fixed now.
Here is the table of bits you should get from eight-bit bytes, with - indicating discarded bit pair, zero 01 and one 10 bit pairs, with most significant bits left. First row corresponds to values 0 to 15, and last row to values 240 to 255: Code:
---- ---0 ---1 ---- --0- --00 --01 --0- --1- --10 --11 --1- ---- ---0 ---1 ---- Code:
42 dd 08 04 53 42 de 96 f7 f7 42 dd 08 04 53 42 Code:
for ((i=0; i<256; i++)) ; do h=$(printf '%02x' $i) ; printf "\x$h" ; done |
Here is my von Neumann randomness extractor, which provides the output described in my post above.
Instead of hardcoding the array, I precalculate it at the beginning of the program. This way the code should compile on architectures with different byte size (as long as it is at most the size of an unsigned int, as I use an unsigned int bit accumulator). The precalculator, init_random(), simply takes each possible input value (unsigned char) and converts it bit pair by bit pair. It consumes and saves ascending bits, starting with the lowest bits first. The value loop is in descending order, but since each value is a self-contained unit, this does not matter. While calculating bit pairs is slow compared to using a lookup, doing it once for each possible input value does not take appreciable time, and allows portable code. Therefore it is worthwhile. You might notice in the inner loop that I handle the bit accumulator has more than a word and bit accumulator has exactly a word cases separately. It turns out that on my architecture and gcc-4.6.1, this is ten percent faster. This version is roughly 5% faster on my architecture than my previous code in this thread, handling about 218000000 bytes of input per second on an Athlon II X4 640. Code:
#include <unistd.h> |
Thanks, I think that fixed it. It now seems to generate equivalent randomness to mine. It is about 7 times faster, so many thanks again :)
I will mark this solved. |
Here's another take on it, if you're interested. It's shorter than the last one Nominal Animal posted, but about 7% slower. I'm pretty sure the difference is due to more stack usage (i.e. less can be taken care of in registers.)
Code:
#include <stdio.h> edit: Actually, it takes 3.5x longer when reading from a file and dumping to /dev/null. Sorry about the false claim! |
Thanks for the other take. I will try to understand it as well :)
I do not understand one thing: Using the test sequence (0-255), the output for mine and ta0kira: Code:
0x00000000: DD42F7F7 96DE4253 0408DD42 F7F796DE ÝB÷÷.ÞBS..ÝB÷÷.Þ Code:
0x00000000: 42DD0804 5342DE96 F7F742DD 08045342 BÝ..SBÞ.÷÷BÝ..SB |
Quote:
Quote:
Kevin Barry |
I see, so it really depends on many things. Still the output is equivalent, so I can use it.
In case anyone is wondering what I'm doing with all this. I hooked up a radio headphone jack to my mic jack using an audio recording cable, something like: http://www.sonicelectronix.com/item_...he-AUX3BL.html I tuned the radio to a noisy channel. This is the hardest part, because noise doesn't always mean randomness. I did tests on the output using ENT and this script: Code:
#!/bin/sh As per the schema I posted in the OP, I first run a von Neumann extractor, any of these will work just fine, but performance differs. Nominal Animal's code is unsurpassed. Then I run a hash on blocks of that output. This was hard to code, probably because I'm not that good at C. However, I have coded a small add-on to the whirlpool hash reference implementation. This is the part that I changed: Code:
int main(void) { I can just pipe the output from the radio like this: Code:
rrec | vnemed | whash > output |
All times are GMT -5. The time now is 04:30 AM. |