Conversion between Factorial Numbers and Permutations

algorithms
imported
Published

January 16, 2015

Conversion methods can be found in http://en.wikipedia.org/wiki/Factorial_number_system A naive algorithm takes \[O(n^2)\], which can be reduced to \[O(nlogn)\] if segment trees are used. Converting a factorial number to a permutation Suppose the length of the factorial number is n. Let Perm be the smallest standard permutation of length n(a.k.a all values are distinct and are from 0 to n-1) Let t[o..n-1] be an array of integers. Initially set t[i]=1 for all \[ 0 \leq i <n\] Let sum[i..j] be the sum of t[i..j]. The crucial part of the original algorithm is to find k-th(zero-based) unused number in Permu, which can be done as follows: Searching for sum[0..j] with smallest j such that sum[0..j]=k+1, after finding the deserved j, set t[j]=0 and update sum array. updating can be done in O(logn) with the help of a segment tree, and modified binary search in the segment tree takes O(logn)

// O(nlogn)
vector<int> fac_to_permu(vector<int> &fac){
  int n=fac.size();
  SegmentTree<int,int> st;
  vector<int> data(n,1);
  st.build_tree(data);
  vector<int> permu(n,0);
  for(int i=0;i<n;i++){
    permu[i]=st.find_idx(fac[n-i-1]+1);
    st.update(permu[i],0);
  }
  return permu;
}

(You can find st.find_idx() in the implementation of segment tree below) Converting a permutation to a factorial number Let p be the permutation. Let l be an empty list. First we scan the permutation backwardly. For each index i, the corresponding factorial digit is the index of p[i] in l (zero-based) after inserting p[i] into a proper place of l such that l is sorted. Again, a naive algorithm takes \[O(n^2)\]. Let t[o..n-1] be an array of integers. Initially set t[i]=0 for all \[ 0 \leq i <n\] Let sum[i..j] be the sum of t[i..j]. For each index i, set t[p[i]]=1, which imples that p[i] has been inserted into a proper place of l, the corresponding factorial digit=sum[0..p[i]] (sum[0..p[i]] basically counts how many numbers there are before p[i] in the sorted list l). With the help of a segment tree, updating t[p[i]]=1 and finding sum[0..p[i]] take O(logn).

// O(nlogn)
vector<int> permu_to_fac(vector<int> &permu){
  int n=permu.size();
  SegmentTree<int,int> st;
  vector<int> fac(n,0),data(n,0);
  st.build_tree(data);
  for(int i=n-1;i>=0;i--){
    fac[n-i-1]=st.query(0,permu[i]);
    st.update(permu[i],1);
  }
  return fac;
}

Segment Tree

template <class Dtype,class Qtype>
class SegmentTree{
private:
  // st size>=2*2^(floor(log2(n))+1) or loosely 4*n
  // default implementation: sum of range[L..R]
  // Dtype: data type
  // Qtype: query type
  Qtype st[800001];
  int n;
  int left(int p){return p<<1;}
  int right(int p){return (p<<1)+1;};
  void build(int p,int L,int R,Dtype data[]){
    if(L==R){
      st[p]=data[L];
    }
    else{
      build(left(p),L,(L+R)/2,data);
      build(right(p),(L+R)/2+1,R,data);
      Qtype l=st[left(p)];
      Qtype r=st[right(p)];
      st[p]=l+r;
    }
  }
  void build(int p,int L,int R,vector<Dtype> &data){
    if(L==R){
      st[p]=data[L];
    }
    else{
      build(left(p),L,(L+R)/2,data);
      build(right(p),(L+R)/2+1,R,data);
      Qtype l=st[left(p)];
      Qtype r=st[right(p)];
      st[p]=l+r;
    }
  }
  bool check(int L,int R,int i,int j){
    return !(i>R||j<L);
  }
  Qtype rmq(int p,int L,int R,int i,int j){
    if(i<=L&&R<=j){
      return st[p];
    }
    bool chk_left=check(L,(L+R)/2,i,j);
    bool chk_right=check((L+R)/2+1,R,i,j);
    if(chk_left&&chk_right){
      return rmq(left(p),L,(L+R)/2,i,j)+rmq(right(p),(L+R)/2+1,R,i,j);
    }
    else if(chk_left){
      return rmq(left(p),L,(L+R)/2,i,j);
    }
    else if(chk_right){
      return rmq(right(p),(L+R)/2+1,R,i,j);
    }
  }
  void _update(int pos,Dtype value,int p,int L,int R){
    if(L==R){
      st[p]=value;
    }
    else{
      int m=(L+R)/2;
      if(pos<=m){
    _update(pos,value,left(p),L,(L+R)/2);
      }
      else{
    _update(pos,value,right(p),(L+R)/2+1,R);
      }
      Qtype l=st[left(p)];
      Qtype r=st[right(p)];
      st[p]=l+r;
    }
  }
  // Extra Functions for the default implementation
  // Warning: For the default implementation only!
  // Binary search for an index where st[index]==targetValue
  // Returns left-most index if multiple valid indices exist
  int _find_idx(Qtype targetValue,int p,int L,int R){
    if(L==R){
      if(st[p]==targetValue){
    return L;
      }
      else{
    return -1;
      }
    }
    if(st[left(p)]>=targetValue){
      return _find_idx(targetValue,left(p),L,(L+R)/2);
    }
    else{
      return _find_idx(targetValue-st[left(p)],right(p),(L+R)/2+1,R);
    }
  }
public:
  void build_tree(int n,Dtype data[]){
    this->n=n;
    build(1,0,n-1,data);
  }
  void build_tree(vector<Dtype> &data){
    this->n=data.size();
    build(1,0,n-1,data);
  }
  Qtype query(int left,int right){
    return rmq(1,0,n-1,left,right);
  }
  void update(int pos,Dtype value){
    _update(pos,value,1,0,n-1);
  }
  // Extra Functions for default implementation
  int find_idx(Qtype targetValue){
    return _find_idx(targetValue,1,0,n-1);
  }
};