0

Having natural numbers 1 to N (N is around 1e7), I dream of function that would reorder the set in a way, defined by a rather short set of parameters, compared to the range of values.

For N = 2^i - 1 this can be just bit reordering, thus, a set of only i values of 0..i defines the mutation.

I am looking for a similarly beautiful way, applicable to an arbitrary N.


The bit reordering example. 8 values: 0..7 are encoded with 3 bits: 000 – 111. To reorder that set I stored each bit's new position. Take an array [0,1,2] and randomly reorder it, then store the result as the permutation key. I.e. [1,0,2] will reorder 8 values like so:

   210   201   
0: 000 - 000 :0
1: 001 - 010 :2
2: 010 - 001 :1
3: 011 - 011 :3
4: 100 - 100 :4
5: 101 - 110 :6
6: 110 - 101 :5
7: 111 - 111 :7
Serge
  • 1,531
  • 2
  • 21
  • 44
  • Could you expand on how, say, when `N = 8 = 2^3`, a set of only 3 values (or is it 4? `0..3` is 4 values) "defines the mutation" ? – AakashM Jul 29 '15 at 07:50
  • @AakashM updated the question with the example. – Serge Jul 29 '15 at 08:02
  • Sounds like need in [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) – Alma Do Jul 29 '15 at 08:36

3 Answers3

3

As identified in comments, you're not interested in being able to encode an arbitrary one of the N! permutations with a short key; you're simply looking for a way to deterministically pick a a permutation given a short key.

I would suggest that all you need to do is

  • pick your favourite pseudo-random number generator;
  • seed it with your known short key
  • use it to pick values from your list of N items
AakashM
  • 62,551
  • 17
  • 151
  • 186
2

Not quite sure if I understand your question, but you can just loop through the numbers from 1..N, and for each step i, generate a pseudo-random number j in the range 1..N, and then swap i and j.

Tore
  • 171
  • 1
  • 4
  • 16
  • I am looking for a short way to define the reordering, so it can be reproduced, based on a short set of parameters, compared to the set itself. – Serge Jul 29 '15 at 07:35
  • So, if you apply the same parameters twice, you get the same reordering? – Tore Jul 29 '15 at 07:38
  • Exactly! I want a short key that allows to reproduce the same reordering. – Serge Jul 29 '15 at 07:48
  • There are N! possible permutations, and you want to reproduce a specific permutation. Each unique permutation can be identified by a number between 1 and N!. I believe there are known algorithms for this where you can pass in a single parameter n between 1 and N! and get the nth permutation. Try to Google "nth lexicographic permutation". – Tore Jul 29 '15 at 07:59
  • Not necessarily any of the possible permutations need to be encoded: happy to sacrifice a huge subset of those in favor of a short key. `1e7!` is a rather large number. – Serge Jul 29 '15 at 08:05
  • 2
    @Serge, you just have to instantiate a new random number generator with the predefined seed (for ex. in Java it's `new Random(seed)`) and use it just for this operation like Tore suggests, then discard this generator. The seed value would be the short parameter you are looking for. – Tagir Valeev Jul 29 '15 at 08:05
  • @Serge, then just agree on a number k, where k is as small or large as you want, and generate the nth permutations where n is between 1 and k. Is that an acceptable subset? – Tore Jul 29 '15 at 08:11
  • 1
    Actually you should generate a pseudo-random number j in the range i..N for a better distribution – Niklas B. Jul 29 '15 at 09:23
2

Simple way which don't consume memory is to multiply each number by the constant which is coprime with N, then calculate the remainder of division by N like this (example in Java):

static int reorder(int input, int N) {
    // 15485863 is prime, thus coprime with any lesser N
    return (int) ((input * 15485863L) % N);
}

Testing with N = 10345560:

public static void main(String[] args) {
    int N = 10345560;
    int[] source = IntStream.range(0, N).toArray();
    int[] reordered = IntStream.of(source).map(i -> reorder(i, N)).toArray();
    // Check that all reordered numbers are within 0..N-1
    assert IntStream.of(reordered).allMatch(i -> i >= 0 && i < N);
    // Check that all numbers are different
    assert IntStream.of(reordered).distinct().count() == N;
}

The advantage is that you don't need intermediate memory to store all the numbers: you may process them in streaming way (for example, reading from one file and writing the result to another).

The disadvantage is that for supplied parameter you have to test that it's coprime with N and reject or adjust it if it's not.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334