专栏文章

next_permutaion 与 Gosper's Hack

个人记录参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioxlw6p
此快照首次捕获于
2025/12/03 02:48
3 个月前
此快照最后确认于
2025/12/03 02:48
3 个月前
查看原文

next_permutation 的实现

CPP
bool nxtp(){
	int i=n-1;
	for(;i>=1;i--)if(a[i]<a[i+1])break;
	if(!i)return 0;
	int j=n;
	for(;j>=1;j--)if(a[i]<a[j])break;
	std::swap(a[i],a[j]);
	std::reverse(a+1+i,a+1+n);
	return 1;
}
没什么多说的,就是上面这么做的。

next_permutaion 均摊复杂度证明

方法:得到每次最长下降后缀长度之和,除以 n!n! 就是答案。
  1. 打表找规律
  2. 递推
  3. 数学计算

打表找规律

使用 nxtp 函数打表,注意力惊人可得:
fi=fi1×i+1f_i=f_{i-1}\times i+1

递推

令当 n=in=i 的时候最长下降后缀长度之和为 fif_i
f1=1f_1=1
fif_i 第一位随便选,所有是 nn,然后 n1n-1 位与 fi1f_{i-1} 相同,但最后会多一份贡献 n,n1,n2,,2,1n,n-1,n-2,\cdots,2,1,前面没考虑到,所以加一。
fi=fi1×i+1f_i=f_{i-1}\times i+1

数学

横看成岭侧成峰\boxed{\text{横看成岭侧成峰}} 某些表格看横再看竖,能有不同发现。
nn 个数里分别选 11 个,22 个,33 个,使得其后面 1,2,31,2,3 个数是递减的。
fi=j=1ngjf_i=\sum\limits^{n}_{j=1} g_j
gj=n!j!g_j=\frac{n!}{j!}

结论

三种方法可得 fi=j=1nn!j!f_i=\sum\limits^{n}_{j=1}\frac{n!}{j!}
所有均摊复杂度是 fin!<e1\frac{f_i}{ n!} \lt e-1
因此均摊时间复杂度为 O(1)O(1)

Gosper's hack

给你一个二进制数 xx,请算出第一个比 xx 大且 11 的数量与 xx 相同的数。
不难想到,做一遍 next_permutaion 即可。
但 Gosper 想出了仅用算数就做出来的方法 O(1)O(1)

Gosper's hack 解法

引用 Deepseek 的解释。

推导公式

现在,让我们尝试从原理上推导出这个公式。我们需要从一个有k个1的二进制数x,生成下一个有k个1的二进制数。

Gosper's Hack 算法详解

1. 找到最右边的可移动 1

设当前数为 xx,其二进制表示为: x=0111100x = \cdots 011\cdots 1\underline{1}00\cdots 其中:
  • 下划线标记的是最右边连续 1 块的最左侧 1
  • 该位置的值可通过低位运算得到:
lowbit=x&(x+1)=x&(x)\text{lowbit} = x \& (\sim x + 1) = x \& (-x)

2. 移动该 1 位

将该 1 向左移动一位: y=x+lowbity = x + \text{lowbit} 效果示例: 00111+000010100000111 \xrightarrow{+00001} 01000

3. 处理右侧的 1

移动后需要补回因进位而丢失的 1:
  1. 计算异或: xy=1111000x \oplus y = \cdots 1111000\cdots
  2. 调整步骤: adjust=(xylowbit)2\text{adjust} = (\frac{x \oplus y}{\text{lowbit}}) \gg 2
先把高位用异或消掉,然后将 1111111111111111 放到结尾,最后会多两个 11,直接右移截断。
至于为何会多两个 11,自己想。

4. 组合结果

最终的下一个数为: next=yadjust\text{next} = y \mid \text{adjust}
步骤数字
xx0011100111
lowbit\text{lowbit}0000100001
y=x+lowbity = x + \text{lowbit}0100001000
xyx \oplus y0111101111
(xy)2(x \oplus y) \gg 20001100011
adjust\text{adjust}0001100011
next\text{next}0101101011

本课要点

关注一切问题\boxed{\text{关注一切问题}}

评论

0 条评论,欢迎与作者交流。

正在加载评论...