这是我在大学时代曾经写过的一门处理玻色子的课程。它很长,但它通常可用并且似乎运行良好。此外,它还提供排名和取消排名功能。希望能有所帮助——但永远不要问我当时在做什么...... ;-)
struct SymmetricIndex
{
using StateType = std::vector<int>;
using IntegerType = int;
int M;
int N;
StateType Nmax;
StateType Nmin;
IntegerType _size;
std::vector<IntegerType> store;
StateType state;
IntegerType _index;
SymmetricIndex() = default;
SymmetricIndex(int _M, int _N, int _Nmax = std::numeric_limits<int>::max(), int _Nmin = 0)
: SymmetricIndex(_M, _N, std::vector<int>(_M + 1, std::min(_Nmax, _N)), StateType(_M + 1, std::max(_Nmin, 0)))
{}
SymmetricIndex(int _M, int _N, StateType const& _Nmax, StateType const& _Nmin)
: N(_N)
, M(_M)
, Nmax(_Nmax)
, Nmin(_Nmin)
, store(addressArray())
, state(M)
, _index(0)
{
reset();
_size = W(M, N);
}
friend std::ostream& operator<<(std::ostream& os, SymmetricIndex const& sym);
SymmetricIndex& reset()
{
return setBegin();
}
bool setBegin(StateType& state, StateType const& Nmax, StateType const& Nmin) const
{
int n = N;
for (int i = 0; i<M; ++i)
{
state[i] = Nmin[i];
n -= Nmin[i];
}
for (int i = 0; i<M; ++i)
{
state[i] = std::min(n + Nmin[i], Nmax[i]);
n -= Nmax[i] - Nmin[i];
if (n <= 0)
break;
}
return true;
}
SymmetricIndex& setBegin()
{
setBegin(state, Nmax, Nmin);
_index = 0;
return *this;
}
bool isBegin() const
{
return _index==0;
}
bool setEnd(StateType& state, StateType const& Nmax, StateType const& Nmin) const
{
int n = N;
for (int i = 0; i < M; ++i)
{
state[i] = Nmin[i];
n -= Nmin[i];
}
for (int i = M - 1; i >= 0; --i)
{
state[i] = std::min(n + Nmin[i], Nmax[i]);
n -= Nmax[i] - Nmin[i];
if (n <= 0)
break;
}
return true;
}
SymmetricIndex& setEnd()
{
setEnd(state, Nmax, Nmin);
_index = _size - 1;
return *this;
}
bool isEnd() const
{
return _index == _size-1;
}
IntegerType index() const
{
return _index;
}
IntegerType rank(StateType const& state) const
{
IntegerType ret = 0;
int n = 0;
for (int i = 0; i < M; ++i)
{
n += state[i];
for (int k = Nmin[i]; k < state[i]; ++k)
ret += store[(n - k) * M + i];
}
return ret;
}
IntegerType rank() const
{
return rank(state);
}
StateType unrank(IntegerType rank) const
{
StateType ret(M);
int n = N;
for (int i = M-1; i >= 0; --i)
{
int ad = 0;
int k = std::min(Nmax[i] - 1, n);
for (int j = Nmin[i]; j <= k; ++j)
ad+=store[(n - j) * M + i];
while (ad > rank && k >= Nmin[i])
{
ad -= store[(n - k) * M + i];
--k;
}
rank -= ad;
ret[i] = k+1;
n -= ret[i];
if (n <= 0)
{
return ret;
}
}
return ret;
}
IntegerType size() const
{
return _size;
}
operator StateType& ()
{
return state;
}
auto operator[](int i) -> StateType::value_type& { return state[i]; }
operator StateType const& () const
{
return state;
}
auto operator[](int i) const -> StateType::value_type const& { return state[i]; }
bool nextState(StateType& state, StateType const& Nmax, StateType const& Nmin) const
{
//co-lexicographical ordering with Nmin and Nmax:
// (1) find first position which can be decreased
// then we have state[k] = Nmin[k] for k in [0,pos]
int pos = M - 1;
for (int k = 0; k < M - 1; ++k)
{
if (state[k] > Nmin[k])
{
pos = k;
break;
}
}
// if nothing found to decrease, return
if (pos == M - 1)
{
return false;
}
// (2) find first position after pos which can be increased
// then we have state[k] = Nmin[k] for k in [0,pos]
int next = 0;
for (int k = pos + 1; k < M; ++k)
{
if (state[k] < Nmax[k])
{
next = k;
break;
}
}
if (next == 0)
{
return false;
}
--state[pos];
++state[next];
// (3) get occupation in [pos,next-1] and set to Nmin[k]
int n = 0;
for (int k = pos; k < next; ++k)
{
n += state[k] - Nmin[k];
state[k] = Nmin[k];
}
// (4) fill up from the start
for (int i = 0; i<M; ++i)
{
if (n <= 0)
break;
int add = std::min(n, Nmax[i] - state[i]);
state[i] += add;
n -= add;
}
return true;
}
SymmetricIndex& operator++()
{
bool inc = nextState(state, Nmax, Nmin);
if (inc) ++_index;
return *this;
}
SymmetricIndex operator++(int)
{
auto ret = *this;
this->operator++();
return ret;
}
bool previousState(StateType& state, StateType const& Nmax, StateType const& Nmin) const
{
////co-lexicographical ordering with Nmin and Nmax:
// (1) find first position which can be increased
// then we have state[k] = Nmax[k] for k in [0,pos-1]
int pos = M - 1;
for (int k = 0; k < M - 1; ++k)
{
if (state[k] < Nmax[k])
{
pos = k;
break;
}
}
// if nothing found to increase, return
if (pos == M - 1)
{
return false;
}
// (2) find first position after pos which can be decreased
// then we have state[k] = Nmin[k] for k in [pos+1,next]
int next = 0;
for (int k = pos + 1; k < M; ++k)
{
if (state[k] > Nmin[k])
{
next = k;
break;
}
}
if (next == 0)
{
return false;
}
++state[pos];
--state[next];
int n = 0;
for (int k = 0; k <= pos; ++k)
{
n += state[k] - Nmin[k];
state[k] = Nmin[k];
}
if (n == 0)
{
return true;
}
for (int i = next-1; i>=0; --i)
{
int add = std::min(n, Nmax[i] - state[i]);
state[i] += add;
n -= add;
if (n <= 0)
break;
}
return true;
}
SymmetricIndex operator--()
{
bool dec = previousState(state, Nmax, Nmin);
if (dec) --_index;
return *this;
}
SymmetricIndex operator--(int)
{
auto ret = *this;
this->operator--();
return ret;
}
int multinomial() const
{
auto v = const_cast<std::remove_reference<decltype(state)>::type&>(state);
return multinomial(v);
}
int multinomial(StateType& state) const
{
int ret = 1;
int n = state[0];
for (int i = 1; i < M; ++i)
{
n += state[i];
ret *= binomial(n, state[i]);
}
return ret;
}
SymmetricIndex& random(StateType const& _Nmin)
{
static std::mt19937 rng;
state = _Nmin;
int n = std::accumulate(std::begin(state), std::end(state), 0);
auto weight = [&](int i) { return state[i] < Nmax[i] ? 1 : 0; };
for (int i = n; i < N; ++i)
{
std::discrete_distribution<int> d(N, 0, N, weight);
++state[d(rng)];
}
_index = rank();
return *this;
}
SymmetricIndex& random()
{
return random(Nmin);
}
private:
IntegerType W(int m, int n) const
{
if (m < 0 || n < 0) return 0;
else if (m == 0 && n == 0) return 1;
else if (m == 0 && n > 0) return 0;
//else if (m > 0 && n < Nmin[m-1]) return 0;
else
{
//static std::map<std::tuple<int, int>, IntegerType> memo;
//auto it = memo.find(std::make_tuple(k, m));
//if (it != std::end(memo))
//{
// return it->second;
//}
IntegerType ret = 0;
for (int i = Nmin[m-1]; i <= std::min(Nmax[m-1], n); ++i)
ret += W(m - 1, n - i);
//memo[std::make_tuple(k, m)] = ret;
return ret;
}
}
IntegerType binomial(int m, int n) const
{
static std::vector<int> store;
if (store.empty())
{
std::function<IntegerType(int, int)> bin = [](int n, int k)
{
int res = 1;
if (k > n - k)
k = n - k;
for (int i = 0; i < k; ++i)
{
res *= (n - i);
res /= (i + 1);
}
return res;
};
store.resize(M*M);
for (int i = 0; i < M; ++i)
{
for (int j = 0; j < M; ++j)
{
store[i*M + j] = bin(i, j);
}
}
}
return store[m*M + n];
}
auto addressArray() const -> std::vector<int>
{
std::vector<int> ret((N + 1) * M);
for (int n = 0; n <= N; ++n)
{
for (int m = 0; m < M; ++m)
{
ret[n*M + m] = W(m, n);
}
}
return ret;
}
};
std::ostream& operator<<(std::ostream& os, SymmetricIndex const& sym)
{
for (auto const& i : sym.state)
{
os << i << " ";
}
return os;
}
像这样使用它
int main()
{
int M=4;
int N=3;
std::vector<int> Nmax(M, N);
std::vector<int> Nmin(M, 0);
Nmax[0]=3;
Nmax[1]=2;
Nmax[2]=1;
Nmax[3]=1;
SymmetricIndex sym(M, N, Nmax, Nmin);
while(!sym.isEnd())
{
std::cout<<sym<<" "<<sym.rank()<<std::endl;
++sym;
}
std::cout<<sym<<" "<<sym.rank()<<std::endl;
}
这将输出
3 0 0 0 0 (corresponds to {40,40,40})
2 1 0 0 1 (-> {40,40,50})
1 2 0 0 2 (-> {40,50,50})
2 0 1 0 3 ...
1 1 1 0 4
0 2 1 0 5
2 0 0 1 6
1 1 0 1 7
0 2 0 1 8
1 0 1 1 9
0 1 1 1 10 (-> {50,60,100})
DEMO
请注意,我在这里假设您的集合元素的升序映射(即数字 40 由索引 0 给出,50 的数目由索引 1 给出,依此类推)。
更准确地说:将您的列表变成map<std::vector<int>, int> 赞
std::vector<int> v{40,40,40,50,50,60,100};
std::map<int, int> m;
for(auto i : v)
{
++m[i];
}
然后使用
int N = 3;
int M = m.size();
std::vector<int> Nmin(M,0);
std::vector<int> Nmax;
std::vector<int> val;
for(auto i : m)
{
Nmax.push_back(m.second);
val.push_back(m.first);
}
SymmetricIndex sym(M, N, Nmax, Nmin);
作为SymmetricIndex 类的输入。
要打印输出,请使用
while(!sym.isEnd())
{
for(int i=0; i<M; ++i)
{
for(int j = 0; j<sym[i]; ++j)
{
std::cout<<val[i]<<" ";
}
}
std::cout<<std::endl;
}
for(int i=0; i<M; ++i)
{
for(int j = 0; j<sym[i]; ++j)
{
std::cout<<val[i]<<" ";
}
}
std::cout<<std::endl;
所有未经测试,但它应该给出的想法。