1

小数を階乗に変換したい。

たとえば、最大100要素の配列のn番目の辞書順列を見つけるためにこれを行いたいです。A[87]={1,2,3..,87}

インデックス 'n' が与えられ、その位置での辞書順列を見つける必要があります。たとえば、{1,2,3} の 2 番目の順列は {1,3,2} です。

このために、階乗数システムを使用しようとしています。

以下のリンクは、変換方法に関する情報を提供します。

https://en.wikipedia.org/wiki/Factorial_number_system

説明したように (463) を 10 進数で表すと 341010 になります! 階乗で。

463 ÷ 1 = 463、余り 0

463 ÷ 2 = 231、余り 1

231 ÷ 3 = 77、余り 0

77 ÷ 4 = 19、余り 1

19 ÷ 5 = 3、余り 4

3 ÷ 6 = 0、余り 3

このメソッドは、10 進数が unsigned long long int のように許容範囲内にある場合にのみ適用できます。

数値が整数の範囲に収まらない場合はどうすればよいですか?

私のテストケースには、文字列形式で保存する必要があるほど大きな数が含まれています.

この問題を c++ で解決しようとしています。

(c++ で next_permutation() を使用して特定のインデックスを取得するのは、コストのかかる方法であり、多くの時間がかかります。)

4

2 に答える 2

0

タイトルには、10 進数を整数に変換する方法を探していると書かれていますが、解決しようとしている実際の問題に対する答えを示します。N 要素の配列の K 番目の順列を取得する方法です。

簡単に言えば、与えられた配列の K 番目の順列を予測するには、1 桁ずつ進む必要があります。物事の理論的な側面は非常に単純です。配列 A に要素があり、各要素が 2 番目の配列 S で使用されているかどうかに関する情報を格納しているとします。各桁に適切な値を選択すると、S が更新されます。結果は配列 R に格納されます。

Nあります!与えられた配列 A の要素の順列。N 桁の配列の場合、A の最小要素が結果の左端の桁 R[0] になるように選択された場合に、いくつの順列があるかを考えてみましょう。(N-1)ですよね?#1 から #(N-1) までの順列です! 結果の左端の要素が A の最小要素である場合に属します。順列 #((N-1)! + 1) から #(2 *(N-1)!) は、A の 2 番目に小さい値を R とします。 [0]。そのため、#((i-1) * (N-1)! + 1) から #(i * (N-1)!) への順列は、A の i 番目の未使用で辞書編集的に最小の数字を R[0] として使用します。

より一般化された意味では、値は K 番目の辞書編集的に最小の順列 A[i] の R[d] で使用されているため、A[i] はこれまで使用されていない i 番目の辞書編集的に最小の要素であり、(i * (N-1-d)! + 1) <= kおよびk <= ((i+1) * (N-1-d)!)。

S 全体をトラバースすると、適切な i 値を見つけるのにO(N)時間がかかります。それを正確に実装する方法についてはわかりませんが、S でバイナリ検索を実行して、適切な値を見つけることもできる場合があります。 i は O(logN) 時間です。

大きな K 値がある場合、比較を行うために大きな整数乗算を実装する必要があると思いますが、これを回避する賢い方法を考えたら、回答のこの部分を更新します。

適切な i を選択したら、A[i] を R[d] に割り当てて、次の数字を見つけることができます。

以下は、このソリューションを実装するコードの一部です。長くなりますが、そのほとんどは単なる大整数の実装です。アルゴリズムの要点は、実際には 30 行未満です。必要に応じて自分でテストできるように、動作するコードを提供したかっただけです。

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#define NLIMIT 100
#define ASIZELIMIT 101
#define BIGINTBUCKETSLIMIT 100
#define BUCKETCAPACITY 1000000000
#define DIGITSPERBUCKET 9

using namespace std;

/* sufficient big integer implementation */
class BigInt
{
    /*
     * Note that BIGINTBUCKETSLIMIT should be high enough so that
     * the values given as input does not cause overflow
     * or access violation from the last bucket in operations
     * multiply and subtract.
     */
public:
    long long buckets[BIGINTBUCKETSLIMIT];

    BigInt() {
        for(int i=0; i < BIGINTBUCKETSLIMIT; ++i) {
            buckets[i] = 0LL;
        }
    }

    BigInt(int initialValue) {
        for(int i=0; i < BIGINTBUCKETSLIMIT; ++i)
        {
            buckets[i] = initialValue % BUCKETCAPACITY;
            initialValue /= BUCKETCAPACITY;
        }
    }

    void multiply(int val) {
        for(int i= BIGINTBUCKETSLIMIT - 1; i >= 0; --i)
            buckets[i] = buckets[i] * val;

        for(int i=0; i < BIGINTBUCKETSLIMIT - 1; ++i) {
            buckets[i+1] += buckets[i] / BUCKETCAPACITY;
            buckets[i] = buckets[i] % BUCKETCAPACITY;
        }
    }

    void subtract(BigInt B) {
        for(int i= 0; i < BIGINTBUCKETSLIMIT; ++i) {
            buckets[i] = buckets[i] - B.buckets[i];
            if(buckets[i] < 0LL) {
                buckets[i] += BUCKETCAPACITY;
                buckets[i+1]--;
            }
        }
    }

    const BigInt & operator=(const BigInt &B) {
        for(int i=0; i < BIGINTBUCKETSLIMIT; ++i)
            buckets[i] = B.buckets[i];
        return *this;
    }

    bool operator<(const BigInt &B) {
        for(int i=BIGINTBUCKETSLIMIT-1; i >= 0; --i)
            if(buckets[i] != B.buckets[i])
                return buckets[i] < B.buckets[i];
        return false;
    }

    void importFromStr(string &src)
    {
        long long buffer = 0, j = 0;
        for(int i=src.size() - 1; i >= 0; i -= DIGITSPERBUCKET) {
            buffer = 0;
            for(int k=max(0, i - DIGITSPERBUCKET + 1); k <= i; ++k) {
                buffer = buffer * 10 + (src[k] - '0');
            }
            buckets[j++] = buffer;
        }
    }
};

BigInt factorials[ASIZELIMIT];

void preprocessFactorials(int n)
{
    factorials[0] = BigInt(1);
    for(int i=1; i <= n; ++i) {
        factorials[i] = factorials[i-1];
        factorials[i].multiply(i);
    }
}

void findKthPermutation(int N, int A[], BigInt K, int result[]) {
    BigInt tmpBigInt;

    bool S[ASIZELIMIT];
    for(int i=0; i < N; ++i)
        S[i] = true;
    K.subtract(BigInt(1));
    preprocessFactorials(N);

    for(int d=0; d < N; ++d) {
        for(int i=0, j=0; i < N; ++i) {
            if(S[i]) {
                tmpBigInt = factorials[N-1-d];
                tmpBigInt.multiply(j+1);
                if(K < tmpBigInt) {
                    result[d] = A[i];
                    S[i] = 0;
                    tmpBigInt = factorials[N-1-d];
                    tmpBigInt.multiply(j);
                    K.subtract(tmpBigInt);
                    break;
                }
                ++j;
            }
        }
    }
}

int main() {
    string k;
    BigInt K;
    int N;
    int A[ASIZELIMIT], R[ASIZELIMIT];

    cin >> N >> k;
    for(int i=0; i < N; ++i)
        cin >> A[i];
    K.importFromStr(k);

    sort(A, A+N);
    findKthPermutation(N, A, K, R);

    cout << R[0];
    for(int i=1; i < N; ++i)
        cout << " " << R[i];
    cout << endl;

    return 0;
}

関数 findKthPermutation と私の BigInt クラスの 2 つのループを簡単に観察できるように、実装は K に関係なく O(N 3 ) で機能します。正確なパフォーマンスのニーズはわかりませんが、N <= 100 であるため、十分に効率的です。あなたが望むほど効率的でない場合、私の最初の提案は、O(logN) 時間で各桁 d に対して求められる適切な i 値を生成する可能性のある他のデータ構造を使用して、S に情報を格納することを最適化することです。

最後に、この解決策では、可能性のある順列の辞書式列挙に干渉するため、 A に重複する要素が含まれていないことを前提としていることに注意してください。

于 2016-03-20T18:33:02.203 に答える
0

これがコードです。ご不明な点がございましたら、お問い合わせください。

また、あなたが提供したテスト ケースは 1 つしかなく、コードの徹底的なテストは行っていません。エラーが見つかった場合は、喜んで解決いたします。

#include<bits/stdc++.h>
using namespace std;

#define MAX 1000000
#define MOD 1000000007
#define F first
#define S second
#define PB push_back
#define MP make_pair
#define V vector
#define I int
#define D double
#define B bool
#define pii pair<int,int>
#define LL long long

#define in(x) scanf("%d",&x)
#define in2(x,y) scanf("%d%d",&x,&y)
#define lin(x) scanf("%lld",&x)
#define lin2(x,y) scanf("%lld%lld",&x,&y)
#define FOR(i,a,b) for(i=a;i<b;i++)
#define all(v) v.begin(),v.end()

string q;    //this is the input
V<I> sol;    //this is final solution vector. (will be printed in reverse)

void divide(I n){    //function to divide a string by `n`
    string r = "";
    I i,k=0;
    FOR(i,0,q.length()){
        k *= 10;
        k += (q[i] - '0');
        I g = k / n;
        k = k % n;
        if((r.length()==0 && g!=0) || (r.length()>0)){
            r += (char)(g + '0');
        }
    }
    q = r;
    sol.PB(k);
}

I main(){
    cin>>q;
    I i;
    FOR(i,1,101){   //assuming 100 is the limit
        if(q.length()==0)
            break;
        divide(i);
    }
    //print the result
    for(i=sol.size()-1;i>=0;i--)
        //printf("%d ",sol[i]);
        cout<<sol[i]<<" ";
    printf("\n");
    return 0;
}
于 2016-03-20T15:34:03.947 に答える