From cube-lovers-errors@oolong.camellia.org Mon Jun 2 22:08:35 1997 Return-Path: cube-lovers-errors@oolong.camellia.org Received: from oolong.camellia.org (localhost [127.0.0.1]) by oolong.camellia.org (8.6.12/8.6.12) with SMTP id WAA04759; Mon, 2 Jun 1997 22:08:34 -0400 Precedence: bulk Errors-To: cube-lovers-errors@oolong.camellia.org Date: Mon, 2 Jun 97 18:58:59 EDT Message-Id: <9706022258.AA28762@sun13.aic.nrl.navy.mil> From: Dan Hoey To: cube-lovers@ai.mit.edu Subject: Indexing (was Re: miscellaneous comments) In-Reply-To: <199706021710.KAA21887@denali.cs.ucla.edu> Rich Korf wrote: > Here's the indexing problem. Write out all the permutations of > say 4 elements, 24 in all, in lexicographic, or any other, > order. Now number the permutations from 0 to 23. The problem then > is given a permutation of N elements, compute its sequential number > in your ordering scheme. The obvious algorithms do this in roughly > N^2 time, but it would be nice to able to do it faster. I thought everyone knew this, but it seems not. The procedure is this: Make a fresh copy of P and its inverse Pinv, represented as arrays on [0..N-1]. For k from N-1 down to 1, do i = Pinv[k]; Pinv[P[k]] = i; P[i] = P[k]. The loop invariant is that P[0..k] is a permutation on [0..k] and Pinv[0..k] is its inverse. Conceptually, you are exchanging P[k-1] with P[Pinv[k-1]] to turn P into the identity permutation. But instead, you leave stuff in the part of the P and Pinv arrays that you don't need to use because you decrement k. That stuff you leave records what exchanges you (would have) made, so it encodes the index in a variable base: 0<=P[k]<=k and you take the sum (P[k] k!) to get the index. The permutation parity is |{k : P[k]==k}| mod 2. This requires O(N) operations on integers of size O(N log N), so the time is O(N^2 log N). But if we don't charge extra for the integer size, it's an O(N) algorithm. If you're using the index to lookup something in a table that exceeds the integer size you usually need to split the index into integer-sized subindices anyway (one tells you which byte in the file, another tells you which file on the disk, another tells you which disk...). Oh, and you can run the algorithm in reverse to convert the variable-base index back into a permutation. (This part doesn't need Pinv). If you fill the P[k] with Random[0..k] and do this, you get a fair shuffle. (I wish programs would randomize their cubes this way. Somehow I never trust the 100 random turns.) I think the only reason people don't think of this balking at the initial overhead of making a copy of P and calculating its inverse. But then we go and spend quadratic time searching for the bits and pieces we need. Dan [ Still working on part 3...]