Hızlı permütasyon -> sayı -> permütasyon haritalama algoritmaları


113

N elementim var. Örnek vermek gerekirse, diyelim ki 7 element, 1234567. 7 olduğunu biliyorum! = Bu 7 elementin 5040 permütasyonu mümkündür.

İki işlevden oluşan hızlı bir algoritma istiyorum:

f (sayı), 0 ile 5039 arasındaki bir sayıyı benzersiz bir permütasyona eşler ve

f '(permütasyon) permütasyonu, oluşturulduğu sayıya geri eşler.

Her permütasyonun kendi benzersiz numarasına sahip olması koşuluyla, sayı ve permütasyon arasındaki yazışmaları umursamıyorum.

Yani, örneğin, işlevlerim olabilir

f(0) = '1234567'
f'('1234567') = 0

Akla gelen en hızlı algoritma, tüm permütasyonları numaralandırmak ve her iki yönde bir arama tablosu oluşturmaktır, böylece tablolar oluşturulduktan sonra, f (0) O (1) ve f ('1234567') bir bir dizede arama. Bununla birlikte, özellikle n büyüdüğünde bu bellek açtır.

Hızlı bir şekilde ve bellek dezavantajı olmadan çalışacak başka bir algoritma önerebilir mi?


Aşağıdaki algoritma çok kapsamlı olsa da en hızlı algoritmanın bir arama tablosu olduğunu doğru bir şekilde söylüyorsunuz. Sisteminize ve platformunuza bağlı olmasına rağmen, gerçekten 'o kadar' bellekten bahsetmiyorsunuz. Ancak bir arama tablosu yeterli olacaksa ve bu gerçek dünya uygulamasıysa onu kullanın. Hızlı ve basit!
Kirk Broadhurst

14
Bunu söylüyorsunuz, ama aptalca olması için n'nin çok büyük olması gerekmez. 12 element için, 12! 479.001.600 permütasyondur. Bu büyük bir arama tablosu!
ijw

Farklı gönderilerle karıştırmayın, farklı anlamlar için n kullanın. Bazıları n ip uzunluğunu, bazıları n ise olası permütasyonların sayısını ifade eder. Büyük O kavramını körü körüne karşılaştırmayın. - Geç gelenler uyarılmalıdır - -
把 友情 留 在 无 盐

Yanıtlar:


157

N öğenin permütasyonunu açıklamak için, ilk öğenin bittiği konum için n olasılığa sahip olduğunuzu görürsünüz, böylece bunu 0 ile n-1 arasında bir sayı ile tanımlayabilirsiniz. Bir sonraki elemanın bittiği konum için n-1 olasılık kaldı, bu yüzden bunu 0 ile n-2 arasında bir sayıyla tanımlayabilirsiniz.
N sayı elde edene kadar vb.

N = 5 için bir örnek olarak, getiriyor permütasyon dikkate abcdeiçin caebd.

  • a, birinci eleman ikinci konumda bitiyor, bu yüzden onu indeks 1 olarak atıyoruz .
  • b3. dizin olan dördüncü konumda sona erer, ancak bu kalan üçüncü konumdur, bu yüzden ona 2 atarız .
  • cher zaman 0 olan ilk kalan konumda biter .
  • d(sadece kalan iki konumdan) 1 olan son kalan konumda sona erer .
  • e0'da dizine alınmış kalan tek konumda sona erer .

Yani dizin dizimiz {1, 2, 0, 1, 0} var .

Artık, örneğin bir ikili sayı içinde 'xyz', z + 2y + 4x anlamına gelir. Ondalık sayı
için z + 10y + 100x olur. Her rakam bir miktar ağırlık ile çarpılır ve sonuçlar toplanır. Ağırlıktaki bariz model elbette ağırlığın w = b ^ k, b sayının tabanı ve k rakamın indeksi olmasıdır. (Her zaman sağdan rakamları sayacağım ve en sağdaki rakam için indeks 0'dan başlayacağım. Aynı şekilde 'ilk' rakamdan bahsederken en sağdaki rakamdan bahsediyorum.)

Nedeni basamak için ağırlıklar bu modeli takip niye k 0'dan basamağı ile temsil edilebilir en yüksek sayı sadece haneli k + 1 kullanmaya ile temsil edilebilir en düşük sayıdan daha tam olarak 1 düşük olması gerektiğidir. İkili değerde 0111, 1000'den küçük olmalıdır. Ondalık olarak 099999, 100000'den küçük olmalıdır.

Değişken tabanlı kodlama
Sonraki sayılar arasındaki aralığın tam olarak 1 olması önemli kuraldır. Bunu gerçekleştirerek, dizin dizimizi değişken tabanlı bir sayı ile temsil edebiliriz . Her hane için taban, o hane için farklı olasılıkların miktarıdır. Ondalık sayı için her basamağın 10 olasılığı vardır, sistemimiz için en sağdaki basamak 1 olasılığa ve en soldaki n olasılığa sahip olacaktır. Ancak en sağdaki rakam (dizimizdeki son sayı) her zaman 0 olduğu için, onu dışarıda bırakıyoruz. Bu, 2'den n'ye kadar bazlarla kaldığımız anlamına gelir. Genel olarak, k'inci basamağın tabanı b [k] = k + 2 olacaktır. K basamağı için izin verilen en yüksek değer h [k] = b [k] - 1 = k + 1'dir.

Basamakların ağırlıkları w [k] ile ilgili kuralımız, i = 0'dan i = k'ye giderken h [i] * w [i] toplamının 1 * w [k + 1] 'e eşit olmasını gerektirir. Yinelenen ifade edilirse, w [k + 1] = w [k] + h [k] * w [k] = w [k] * (h [k] + 1). İlk ağırlık w [0] her zaman 1 olmalıdır. Buradan başlayarak şu değerlere sahibiz:

k    h[k] w[k]    

0    1    1  
1    2    2    
2    3    6    
3    4    24   
...  ...  ...
n-1  n    n!  

(W [k-1] = k! Genel ilişkisi, tümevarım ile kolayca kanıtlanır.)

Dizimizi dönüştürerek elde ettiğimiz sayı, k 0'dan n-1'e koşarken, s [k] * w [k] toplamı olacaktır. Burada s [k], dizinin k'inci (en sağdaki, 0'dan başlayarak) öğesidir. Örnek olarak, daha önce belirtildiği gibi en sağdaki öğenin çıkarıldığı {1, 2, 0, 1, 0} öğemizi alın: {1, 2, 0, 1} . Toplamımız 1 * 1 + 0 * 2 + 2 * 6 + 1 * 24 = 37 .

Her dizin için maksimum konumu alırsak, {4, 3, 2, 1, 0} elde edeceğimizi ve bunun 119'a dönüşeceğini unutmayın. Sayı kodlamamızdaki ağırlıklar, atlamamamız için seçildiğinden herhangi bir sayı, 0 ile 119 arasındaki tüm sayılar geçerlidir. Bunlardan tam olarak 120 tane var, ki bu n! Örneğimizde n = 5 için, tam olarak farklı permütasyonların sayısı. Böylece kodlanmış sayılarımızın tüm olası permütasyonları tamamen belirttiğini görebilirsiniz.

Değişken tabanlı
Kod Çözme'den kod çözme, ikili veya ondalık sayıya dönüştürmeye benzer. Ortak algoritma şudur:

int number = 42;
int base = 2;
int[] bits = new int[n];

for (int k = 0; k < bits.Length; k++)
{
    bits[k] = number % base;
    number = number / base;
}

Değişken tabanlı numaramız için:

int n = 5;
int number = 37;

int[] sequence = new int[n - 1];
int base = 2;

for (int k = 0; k < sequence.Length; k++)
{
    sequence[k] = number % base;
    number = number / base;

    base++; // b[k+1] = b[k] + 1
}

Bu doğru {1, 2, 0, 1} bizim 37 geri çözer ( sequenceolurdu {1, 0, 2, 1}bu kod örneğinde, ama ne olursa olsun ... sürece endeks olarak uygun). Orijinal dizimizi {1, 2, 0, 1, 0} geri almak için sağ uca 0 eklememiz gerekir (son elemanın yeni konumu için her zaman sadece bir olasılığı olduğunu unutmayın).

Bir dizin dizisi kullanarak bir
listeye izin verme Belirli bir dizin sırasına göre bir listeye izin vermek için aşağıdaki algoritmayı kullanabilirsiniz. Ne yazık ki bu bir O (n²) algoritması.

int n = 5;
int[] sequence = new int[] { 1, 2, 0, 1, 0 };
char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];
bool[] set = new bool[n];

for (int i = 0; i < n; i++)
{
    int s = sequence[i];
    int remainingPosition = 0;
    int index;

    // Find the s'th position in the permuted list that has not been set yet.
    for (index = 0; index < n; index++)
    {
        if (!set[index])
        {
            if (remainingPosition == s)
                break;

            remainingPosition++;
        }
    }

    permuted[index] = list[i];
    set[index] = true;
}

Permütasyonların ortak temsili
Normalde, bizim yaptığımız kadar beklenmedik bir şekilde bir permütasyonu temsil etmezsiniz, sadece permütasyon uygulandıktan sonra her bir elementin mutlak konumu ile temsil edersiniz. Örneğimiz {1, 2, 0, 1, 0} for abcdeto caebdnormalde {1, 3, 0, 4, 2} ile temsil edilir. 0'dan 4'e (veya genel olarak, 0'dan n-1'e) her bir indeks, bu gösterimde tam olarak bir kez meydana gelir.

Bu formda bir permütasyon uygulamak kolaydır:

int[] permutation = new int[] { 1, 3, 0, 4, 2 };

char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];

for (int i = 0; i < n; i++)
{
    permuted[permutation[i]] = list[i];
}

Tersine çevirmek çok benzer:

for (int i = 0; i < n; i++)
{
    list[i] = permuted[permutation[i]];
}

Temsilimizden ortak temsile dönüştürme
Algoritmamızı indeks dizimizi kullanarak bir listeye izin vermek için alırsak ve bunu {0, 1, 2, ..., n-1} kimlik permütasyonuna uygularsak, şunu elde ederiz: ortak biçimde temsil edilen ters permütasyon. ( Örneğimizde {2, 0, 4, 1, 3} ).

Tersine çevrilmemiş ön mutasyonu elde etmek için az önce gösterdiğim permütasyon algoritmasını uyguluyoruz:

int[] identity = new int[] { 0, 1, 2, 3, 4 };
int[] inverted = { 2, 0, 4, 1, 3 };
int[] normal = new int[n];

for (int i = 0; i < n; i++)
{
    normal[identity[i]] = list[i];
}

Veya ters permütasyon algoritmasını kullanarak permütasyonu doğrudan uygulayabilirsiniz:

char[] list = new char[] { 'a', 'b', 'c', 'd', 'e' };
char[] permuted = new char[n];

int[] inverted = { 2, 0, 4, 1, 3 };

for (int i = 0; i < n; i++)
{
    permuted[i] = list[inverted[i]];
}

Ortak formdaki permütasyonlarla uğraşmak için tüm algoritmaların O (n) olduğunu, formumuzda bir permütasyon uygulamak için O (n²) olduğunu unutmayın. Birkaç kez bir permütasyon uygulamanız gerekiyorsa, önce onu ortak temsile dönüştürün.


6
"Bir dizin dizisi kullanarak bir listeye izin verme" bölümünde, ikinci dereceden bir algoritmadan bahsediyorsunuz. Bu kesinlikle iyi çünkü n muhtemelen çok küçük olacak. Bu, bir sıra istatistik ağacı ( pine.cs.yale.edu/pinewiki/OrderStatisticsTree ), yani başlangıçta 0, 1, 2 değerlerini içeren kırmızı-siyah bir ağaç aracılığıyla "kolayca" O (nlogn) 'a indirgenebilir. , ..., n-1 ve her düğüm, altındaki torunların sayısını içerir. Bununla k'inci eleman O (logn) zamanında bulunabilir / kaldırılabilir.
Dimitris Andreou

11
Bunlara lehmer kodları denir. Bu bağlantı onları da iyi açıklıyor, keithschwarz.com/interesting/code/?dir=factoradic-permutation
mihirg

Bu algoritma harika, ancak birkaç durumun yanlış olduğunu buldum. "123" dizesini alın; 4. permütasyon 231 olmalıdır, ancak bu algoritmaya göre 312 olacaktır. Diyelim ki 1234, 4. permütasyon 1342 olmalı, ancak "1423" olarak yanlış anlaşılacak. Yanlış görürsem düzeltin. Teşekkürler.
Isaac Li

@IsaacLi, eğer doğruysam, f (4) = {2, 0, 0} = 231. Ve f '(312) = {1, 1, 0} = 3. İçin 1234, f (4) = {0, 2, 0, 0} = 1342. Ve f '(1423) = {0, 1 1, 0} = 3. Bu algoritma gerçekten ilham verici. OP'nin orijinal çalışması olduğunu merak ediyorum. Bir süre inceledim ve analiz ettim. Ve doğru olduğuna inanıyorum :)
midnite

"Bizim temsilimiz" den "ortak temsil", {1, 2, 0, 1, 0}-> nasıl dönüştürülür {1, 3, 0, 4, 2}? Ve tam tersi? Mümkün mü? (tarafından değil arasındaki dönüştürme {1, 2, 0, 1, 0}<-> {C, A, E, B, D}(n ^ 2) O ihtiyacı olan.) "Bizim stili" ve "ortak stil" konvertibl değilse, bunlar aslında iki farklı ayrı şeylerdir, değil mi? Teşekkürler x
midnite

18

Bir O (n) algoritması buldum, işte kısa bir açıklama http://antoinecomeau.blogspot.ca/2014/07/mapping-between-permutations-and.html

public static int[] perm(int n, int k)
{
    int i, ind, m=k;
    int[] permuted = new int[n];
    int[] elems = new int[n];

    for(i=0;i<n;i++) elems[i]=i;

    for(i=0;i<n;i++)
    {
            ind=m%(n-i);
            m=m/(n-i);
            permuted[i]=elems[ind];
            elems[ind]=elems[n-i-1];
    }

    return permuted;
}

public static int inv(int[] perm)
{
    int i, k=0, m=1;
    int n=perm.length;
    int[] pos = new int[n];
    int[] elems = new int[n];

    for(i=0;i<n;i++) {pos[i]=i; elems[i]=i;}

    for(i=0;i<n-1;i++)
    {
            k+=m*pos[perm[i]];
            m=m*(n-i);
            pos[elems[n-i-1]]=pos[perm[i]];
            elems[pos[perm[i]]]=elems[n-i-1];
    }

    return k;
}

1
Algoritmanızı çok iyi anlarsam. Kodlanmış tüm olasılıkları buluyorsunuz (Bu durumda, n! Olasılıklar olmalıdır). Ardından, kodlanmış öğeye göre numaraları eşlersiniz.
user3378649

Bloguma kısa bir açıklama ekledim.
Antoine Comeau

1
Bu son derece temiz. Bugün aynı yöntemi kendi kendime buldum, ancak tersinde iki görevi atlayabileceğinizi kaçırdım.
2016

Büyük O kavramını körü körüne karşılaştırmayın, çünkü bu yanıttaki n diğer bazı yanıtlarla aynı değildir - @ user3378649'un işaret ettiği gibi - dizi uzunluğunun faktöriyeliyle bir karmaşıklık oranını belirtir. Bu cevap gerçekten daha az etkilidir.
把 友情 留 在 无 盐

Bu, sözlüksel düzene uyarlanabilir mi?
Gregory Morse

7

Karmaşıklık n * log (n) 'ye indirilebilir, bkz. Fxtbook bölüm 10.1.1 ("Lehmer kodu (ters çevirme tablosu)", s.232ff): http://www.jjj.de/fxt/ #fxtbook hızlı bir yöntem için (s.235 "büyük dizilerle hesaplama") bölümüne 10.1.1.1 geçin. (GPLed, C ++) kodu aynı web sayfasındadır.


5

Sorun çözüldü. Ancak, bu yıllardan sonra hala çözüme ihtiyacınız olduğundan emin değilim. LOL, bu siteye yeni katılıyorum, bu yüzden ... Java Permütasyon Sınıfımı kontrol edin. Bir sembol permütasyonu elde etmek için bir indeksi temel alabilir veya bir sembol permütasyonu verdikten sonra indeksi alabilirsiniz.

İşte Premutasyon Sınıfım

/**
 ****************************************************************************************************************
 * Copyright 2015 Fred Pang fred@pnode.com
 ****************************************************************************************************************
 * A complete list of Permutation base on an index.
 * Algorithm is invented and implemented by Fred Pang fred@pnode.com
 * Created by Fred Pang on 18/11/2015.
 ****************************************************************************************************************
 * LOL this is my first Java project. Therefore, my code is very much like C/C++. The coding itself is not
 * very professional. but...
 *
 * This Permutation Class can be use to generate a complete list of all different permutation of a set of symbols.
 * nPr will be n!/(n-r)!
 * the user can input       n = the number of items,
 *                          r = the number of slots for the items,
 *                          provided n >= r
 *                          and a string of single character symbols
 *
 * the program will generate all possible permutation for the condition.
 *
 * Say if n = 5, r = 3, and the string is "12345", it will generate sll 60 different permutation of the set
 * of 3 character strings.
 *
 * The algorithm I used is base on a bin slot.
 * Just like a human or simply myself to generate a permutation.
 *
 * if there are 5 symbols to chose from, I'll have 5 bin slot to indicate which symbol is taken.
 *
 * Note that, once the Permutation object is initialized, or after the constructor is called, the permutation
 * table and all entries are defined, including an index.
 *
 * eg. if pass in value is 5 chose 3, and say the symbol string is "12345"
 * then all permutation table is logically defined (not physically to save memory).
 * It will be a table as follows
 *  index  output
 *      0   123
 *      1   124
 *      2   125
 *      3   132
 *      4   134
 *      5   135
 *      6   143
 *      7   145
 *      :     :
 *      58  542
 *      59  543
 *
 * all you need to do is call the "String PermGetString(int iIndex)" or the "int[] PermGetIntArray(int iIndex)"
 * function or method with an increasing iIndex, starting from 0 to getiMaxIndex() - 1. It will return the string
 * or the integer array corresponding to the index.
 *
 * Also notice that in the input string is "12345" of  position 01234, and the output is always in accenting order
 * this is how the permutation is generated.
 *
 * ***************************************************************************************************************
 * ====  W a r n i n g  ====
 * ***************************************************************************************************************
 *
 * There is very limited error checking in this class
 *
 * Especially the  int PermGetIndex(int[] iInputArray)  method
 * if the input integer array contains invalid index, it WILL crash the system
 *
 * the other is the string of symbol pass in when the object is created, not sure what will happen if the
 * string is invalid.
 * ***************************************************************************************************************
 *
 */
public class Permutation
{
    private boolean bGoodToGo = false;      // object status
    private boolean bNoSymbol = true;
    private BinSlot slot;                   // a bin slot of size n (input)
    private int nTotal;                     // n number for permutation
    private int rChose;                     // r position to chose
    private String sSymbol;                 // character string for symbol of each choice
    private String sOutStr;
    private int iMaxIndex;                  // maximum index allowed in the Get index function
    private int[] iOutPosition;             // output array
    private int[] iDivisorArray;            // array to do calculation

    public Permutation(int inCount, int irCount, String symbol)
    {
        if (inCount >= irCount)
        {
            // save all input values passed in
            this.nTotal = inCount;
            this.rChose = irCount;
            this.sSymbol = symbol;

            // some error checking
            if (inCount < irCount || irCount <= 0)
                return;                                 // do nothing will not set the bGoodToGo flag

            if (this.sSymbol.length() >= inCount)
            {
                bNoSymbol = false;
            }

            // allocate output storage
            this.iOutPosition = new int[this.rChose];

            // initialize the bin slot with the right size
            this.slot = new BinSlot(this.nTotal);

            // allocate and initialize divid array
            this.iDivisorArray = new int[this.rChose];

            // calculate default values base on n & r
            this.iMaxIndex = CalPremFormula(this.nTotal, this.rChose);

            int i;
            int j = this.nTotal - 1;
            int k = this.rChose - 1;

            for (i = 0; i < this.rChose; i++)
            {
                this.iDivisorArray[i] = CalPremFormula(j--, k--);
            }
            bGoodToGo = true;       // we are ready to go
        }
    }

    public String PermGetString(int iIndex)
    {
        if (!this.bGoodToGo) return "Error: Object not initialized Correctly";
        if (this.bNoSymbol) return "Error: Invalid symbol string";
        if (!this.PermEvaluate(iIndex)) return "Invalid Index";

        sOutStr = "";
        // convert string back to String output
        for (int i = 0; i < this.rChose; i++)
        {
            String sTempStr = this.sSymbol.substring(this.iOutPosition[i], iOutPosition[i] + 1);
            this.sOutStr = this.sOutStr.concat(sTempStr);
        }
        return this.sOutStr;
    }

    public int[] PermGetIntArray(int iIndex)
    {
        if (!this.bGoodToGo) return null;
        if (!this.PermEvaluate(iIndex)) return null ;
        return this.iOutPosition;
    }

    // given an int array, and get the index back.
    //
    //  ====== W A R N I N G ======
    //
    // there is no error check in the array that pass in
    // if any invalid value in the input array, it can cause system crash or other unexpected result
    //
    // function pass in an int array generated by the PermGetIntArray() method
    // then return the index value.
    //
    // this is the reverse of the PermGetIntArray()
    //
    public int PermGetIndex(int[] iInputArray)
    {
        if (!this.bGoodToGo) return -1;
        return PermDoReverse(iInputArray);
    }


    public int getiMaxIndex() {
    return iMaxIndex;
}

    // function to evaluate nPr = n!/(n-r)!
    public int CalPremFormula(int n, int r)
    {
        int j = n;
        int k = 1;
        for (int i = 0; i < r; i++, j--)
        {
            k *= j;
        }
        return k;
    }


//  PermEvaluate function (method) base on an index input, evaluate the correspond permuted symbol location
//  then output it to the iOutPosition array.
//
//  In the iOutPosition[], each array element corresponding to the symbol location in the input string symbol.
//  from location 0 to length of string - 1.

    private boolean PermEvaluate(int iIndex)
    {
        int iCurrentIndex;
        int iCurrentRemainder;
        int iCurrentValue = iIndex;
        int iCurrentOutSlot;
        int iLoopCount;

        if (iIndex >= iMaxIndex)
            return false;

        this.slot.binReset();               // clear bin content
        iLoopCount = 0;
        do {
            // evaluate the table position
            iCurrentIndex = iCurrentValue / this.iDivisorArray[iLoopCount];
            iCurrentRemainder = iCurrentValue % this.iDivisorArray[iLoopCount];

            iCurrentOutSlot = this.slot.FindFreeBin(iCurrentIndex);     // find an available slot
            if (iCurrentOutSlot >= 0)
                this.iOutPosition[iLoopCount] = iCurrentOutSlot;
            else return false;                                          // fail to find a slot, quit now

            this.slot.setStatus(iCurrentOutSlot);                       // set the slot to be taken
            iCurrentValue = iCurrentRemainder;                          // set new value for current value.
            iLoopCount++;                                               // increase counter
        } while (iLoopCount < this.rChose);

        // the output is ready in iOutPosition[]
        return true;
    }

    //
    // this function is doing the reverse of the permutation
    // the input is a permutation and will find the correspond index value for that entry
    // which is doing the opposit of the PermEvaluate() method
    //
    private int PermDoReverse(int[] iInputArray)
    {
        int iReturnValue = 0;
        int iLoopIndex;
        int iCurrentValue;
        int iBinLocation;

        this.slot.binReset();               // clear bin content

        for (iLoopIndex = 0; iLoopIndex < this.rChose; iLoopIndex++)
        {
            iCurrentValue = iInputArray[iLoopIndex];
            iBinLocation = this.slot.BinCountFree(iCurrentValue);
            this.slot.setStatus(iCurrentValue);                          // set the slot to be taken
            iReturnValue = iReturnValue + iBinLocation * this.iDivisorArray[iLoopIndex];
        }
        return iReturnValue;
    }


    /*******************************************************************************************************************
     *******************************************************************************************************************
     * Created by Fred on 18/11/2015.   fred@pnode.com
     *
     * *****************************************************************************************************************
     */
    private static class BinSlot
    {
        private int iBinSize;       // size of array
        private short[] eStatus;    // the status array must have length iBinSize

        private BinSlot(int iBinSize)
        {
            this.iBinSize = iBinSize;               // save bin size
            this.eStatus = new short[iBinSize];     // llocate status array
        }

        // reset the bin content. no symbol is in use
        private void binReset()
        {
            // reset the bin's content
            for (int i = 0; i < this.iBinSize; i++) this.eStatus[i] = 0;
        }

        // set the bin position as taken or the number is already used, cannot be use again.
        private void  setStatus(int iIndex) { this.eStatus[iIndex]= 1; }

        //
        // to search for the iIndex th unused symbol
        // this is important to search through the iindex th symbol
        // because this is how the table is setup. (or the remainder means)
        // note: iIndex is the remainder of the calculation
        //
        // for example:
        // in a 5 choose 3 permutation symbols "12345",
        // the index 7 item (count starting from 0) element is "1 4 3"
        // then comes the index 8, 8/12 result 0 -> 0th symbol in symbol string = '1'
        // remainder 8. then 8/3 = 2, now we need to scan the Bin and skip 2 unused bins
        //              current the bin looks 0 1 2 3 4
        //                                    x o o o o     x -> in use; o -> free only 0 is being used
        //                                      s s ^       skipped 2 bins (bin 1 and 2), we get to bin 3
        //                                                  and bin 3 is the bin needed. Thus symbol "4" is pick
        // in 8/3, there is a remainder 2 comes in this function as 2/1 = 2, now we have to pick the empty slot
        // for the new 2.
        // the bin now looks 0 1 2 3 4
        //                   x 0 0 x 0      as bin 3 was used by the last value
        //                     s s   ^      we skip 2 free bins and the next free bin is bin 4
        //                                  therefor the symbol "5" at the symbol array is pick.
        //
        // Thus, for index 8  "1 4 5" is the symbols.
        //
        //
        private int FindFreeBin(int iIndex)
        {
            int j = iIndex;

            if (j < 0 || j > this.iBinSize) return -1;               // invalid index

            for (int i = 0; i < this.iBinSize; i++)
            {
                if (this.eStatus[i] == 0)       // is it used
                {
                    // found an empty slot
                    if (j == 0)                 // this is a free one we want?
                        return i;               // yes, found and return it.
                    else                        // we have to skip this one
                        j--;                    // else, keep looking and count the skipped one
                }
            }
            assert(true);           // something is wrong
            return -1;              // fail to find the bin we wanted
        }

        //
        // this function is to help the PermDoReverse() to find out what is the corresponding
        // value during should be added to the index value.
        //
        // it is doing the opposite of int FindFreeBin(int iIndex) method. You need to know how this
        // FindFreeBin() works before looking into this function.
        //
        private int BinCountFree(int iIndex)
        {
            int iRetVal = 0;
            for (int i = iIndex; i > 0; i--)
            {
                if (this.eStatus[i-1] == 0)       // it is free
                {
                    iRetVal++;
                }
            }
            return iRetVal;
        }
    }
}
// End of file - Permutation.java

ve işte sınıfın nasıl kullanılacağını gösteren Ana Sınıfım.

/*
 * copyright 2015 Fred Pang
 *
 * This is the main test program for testing the Permutation Class I created.
 * It can be use to demonstrate how to use the Permutation Class and its methods to generate a complete
 * list of a permutation. It also support function to get back the index value as pass in a permutation.
 *
 * As you can see my Java is not very good. :)
 * This is my 1st Java project I created. As I am a C/C++ programmer for years.
 *
 * I still have problem with the Scanner class and the System class.
 * Note that there is only very limited error checking
 *
 *
 */

import java.util.Scanner;

public class Main
{
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args)
    {
        Permutation perm;       // declear the object
        String sOutString = "";
        int nCount;
        int rCount;
        int iMaxIndex;

        // Get user input
        System.out.println("Enter n: ");
        nCount = scanner.nextInt();

        System.out.println("Enter r: ");
        rCount = scanner.nextInt();

        System.out.println("Enter Symbol: ");
        sOutString = scanner.next();

        if (sOutString.length() < rCount)
        {
            System.out.println("String too short, default to numbers");
            sOutString = "";
        }

        // create object with user requirement
        perm = new Permutation(nCount, rCount, sOutString);

        // and print the maximum count
        iMaxIndex = perm.getiMaxIndex();
        System.out.println("Max count is:" + iMaxIndex);

        if (!sOutString.isEmpty())
        {
            for (int i = 0; i < iMaxIndex; i++)
            {   // print out the return permutation symbol string
                System.out.println(i + " " + perm.PermGetString(i));
            }
        }
        else
        {
            for (int i = 0; i < iMaxIndex; i++)
            {
                System.out.print(i + " ->");

                // Get the permutation array
                int[] iTemp = perm.PermGetIntArray(i);

                // print out the permutation
                for (int j = 0; j < rCount; j++)
                {
                    System.out.print(' ');
                    System.out.print(iTemp[j]);
                }

                // to verify my PermGetIndex() works. :)
                if (perm.PermGetIndex(iTemp)== i)
                {
                    System.out.println(" .");
                }
                else
                {   // oops something is wrong :(
                    System.out.println(" ***************** F A I L E D *************************");
                    assert(true);
                    break;
                }
            }
        }
    }
}
//
// End of file - Main.java

İyi eğlenceler. :)


4

Her eleman yedi pozisyondan birinde olabilir. Bir elemanın konumunu tanımlamak için üç bite ihtiyacınız olacaktır. Bu, tüm öğelerin konumunu 32 bit değerinde saklayabileceğiniz anlamına gelir. Bu, verimli olmaktan uzaktır, çünkü bu gösterim tüm öğelerin aynı konumda olmasına izin verir, ancak bit maskelemesinin makul derecede hızlı olması gerektiğine inanıyorum.

Ancak, 8'den fazla pozisyonla daha şık bir şeye ihtiyacınız olacak.


Bu, OP'nin numaralandırmanın gerçekten 0'dan 5039'a gidip gitmediğini umursamadığını varsayar, değil mi? Sorun değilse, bu mükemmel bir çözüm gibi görünüyor.
Troubadour

4

Bu, J'de yerleşik bir işlevdir :

   A. 1 2 3 4 5 6 7
0
   0 A. 1 2 3 4 5 6 7
1 2 3 4 5 6 7

   ?!7
5011
   5011 A. 1 2 3 4 5 6 7
7 6 4 5 1 3 2
   A. 7 6 4 5 1 3 2
5011

2

Özyinelemeli bir algoritma kullanarak permütasyonları kodlayabilirsiniz. Bir N-permütasyonu ({0, .., N-1} sayılarının bazı sıralaması) {x, ...} biçimindeyse, bunu x + N * olarak kodlayın (N-1) -{0, N-1} - {x} sayıları üzerinde "..." ile temsil edilenpermütasyon. Ağız dolusu gibi geliyor, işte bazı kodlar:

// perm[0]..perm[n-1] must contain the numbers in {0,..,n-1} in any order.
int permToNumber(int *perm, int n) {
  // base case
  if (n == 1) return 0;

  // fix up perm[1]..perm[n-1] to be a permutation on {0,..,n-2}.
  for (int i = 1; i < n; i++) {
    if (perm[i] > perm[0]) perm[i]--;
  }

  // recursively compute
  return perm[0] + n * permToNumber(perm + 1, n - 1);
}

// number must be >=0, < n!
void numberToPerm(int number, int *perm, int n) {
  if (n == 1) {
    perm[0] = 0;
    return;
  }
  perm[0] = number % n;
  numberToPerm(number / n, perm + 1, n - 1);

  // fix up perm[1] .. perm[n-1]
  for (int i = 1; i < n; i++) {
    if (perm[i] >= perm[0]) perm[i]++;
  }
}

Bu algoritma O (n ^ 2) 'dir. Herhangi birinin O (n) algoritması varsa bonus puanları.


1

Ne ilginç bir soru!

Tüm öğeleriniz sayı ise, bunları dizelerden gerçek sayılara dönüştürmeyi düşünebilirsiniz. O zaman tüm permütasyonları sırayla sıralayabilir ve bir diziye yerleştirebilirsiniz. Bundan sonra, orada bulunan çeşitli arama algoritmalarından herhangi birine açık olacaksınız.


1

Önceki cevabımda aceleciydim (silindi), yine de gerçek cevaba sahibim. Benzer bir kavram olan faktöradik tarafından sağlanır ve permütasyonlarla ilgilidir (kombinasyonlarla ilgili cevabım, bu karışıklık için özür dilerim). Sadece wikipedia bağlantılarını yayınlamaktan nefret ediyorum, ancak bir süre önce yaptığım yazım bazı nedenlerden dolayı anlaşılmaz. Bu nedenle, istenirse bunu daha sonra genişletebilirim.


1

Bununla ilgili yazılmış bir kitap var. Üzgünüm ama adını hatırlamıyorum (büyük ihtimalle Wikipedia'dan bulacaksınız). ama yine de bu numaralandırma sisteminin bir python uygulaması yazdım: http://kks.cabal.fi/Kombinaattori Bazıları Fince, ancak sadece kodu ve isim değişkenlerini kopyalayın ...


0

Tam olarak şu sorum vardı ve Python çözümümü sağlayacağımı düşündüm. O (n ^ 2).

import copy

def permute(string, num):
    ''' generates a permutation '''
    def build_s(factoradic): # Build string from factoradic in list form
        string0 = copy.copy(string)
        n = []
        for i in range(len(factoradic)):
            n.append(string0[factoradic[i]])
            del string0[factoradic[i]]
        return n

    f = len(string)
    factoradic = []
    while(f != 0): # Generate factoradic number list
        factoradic.append(num % f)
        num = (num - factoradic[-1])//f
        f -= 1

    return build_s(factoradic)

s = set()
# Print 120 permutations of this string
for i in range(120):
    m = permute(list('abcde'), i)
    s.add(''.join(m))

print(len(s)) # Check that we have 120 unique permutations

Oldukça yalındır; Sayının faktöradik temsilini oluşturduktan sonra, karakterleri dizeden seçip kaldırıyorum. Dizeden silme, bunun neden bir O (n ^ 2) çözümü olduğudur.

Antoine'ın çözümü performans için daha iyidir.


-1

İlgili bir soru, yalnızca permütasyon dizisi bilindiğinde permütasyonlu vektörleri orijinal sıraya geri yükleyen bir permütasyon olan ters permütasyonu hesaplamaktır. İşte O (n) kodu (PHP'de):

// Compute the inverse of a permutation
function GetInvPerm($Perm)
    {
    $n=count($Perm);
    $InvPerm=[];
    for ($i=0; $i<$n; ++$i)
        $InvPerm[$Perm[$i]]=$i;
    return $InvPerm;
    } // GetInvPerm

David Spector Springtime Yazılımı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.