C语言名题百则 3.1 列出所有子集 (direct.c)

C语言名题精选百则:所有子集,字典子集,Gray子集_#include

分析:每一个元素只有两种可能,在子集中和不在子集中。

#include <iostream>
#include <cstdio>
using namespace std;
int p[20],top;
int main()
{
int n;
while(cin>>n){
for(int i=0;i<(1<<n);i++){
top=0;
for(int j=0;j<n;j++){
if(i&(1<<j)) p[top++]=j+1;
}
for(int j=0;j<top;j++){
printf("%3d",p[j]);
}
puts("");
}
}
return 0;
}


C语言名题百则 3.2 列出所有子集——字典序列 (lexical.c)

C语言名题精选百则:所有子集,字典子集,Gray子集_子集_02

分析:抓住字典序升序的规律。

#include <iostream>
#include <cstdio>
using namespace std;
int p[25],n;
void show(int top){
for(int i=1;i<=top;i++) printf("%3d",p[i]);
puts("");
}
int main()
{
while(cin>>n){
printf("\n");
int top=0;
for(int i=1;i<(1<<n);i++){
if(p[top]<n){
p[top+1]=p[top]+1;
top++;
show(top);
}
else if(p[top]==n){
p[top-1]++;
top--;
show(top);
}
}
}
return 0;
}


C语言名题百则 3.3 产生Gray码 (Graycode.c)

C语言名题精选百则:所有子集,字典子集,Gray子集_i++_03


C语言名题精选百则:所有子集,字典子集,Gray子集_i++_04


格雷码的特点:虽然自然二进制码可以直接由数/模转换器转换成模拟信号,但在某些情况,例如从十进制的3转换为4时二进制码的每一位都要变,能使数字电路产生很大的尖峰电流脉冲。而格雷码则没有这一缺点,它在相邻位间转换时,只有一位产生变化。


典型格雷码是一种具有反射特性和循环特性的单步自补码,它的循环、单步特性消除了随机取数时出现重大误差的可能,它的反射、自补特性使得求反非常方便。



现在又看这种生成格雷码的方法,不禁想问,它是怎样被发现的?

C语言名题精选百则:所有子集,字典子集,Gray子集_#include_05



对照自然二进制数和格雷码可以发现,不看自然二进制和格雷码的最高位,它们剩下的部分进行异或运算所得到的结果刚好是二进制前n-1位。例如12对应的1100~1010,100^010=110。那么反推,十进制数字对应的格雷码的最高位和二进制最高位一样,剩下的部分就该是二进制后n-1位和前n-1位进行异或运算得到。即是二进制转成格雷码的方法。【异或运算的特点: if c=a^b  so  a=c^b  and b=a^c 】
异或运算的特点让我想起了那个加密解密的例子:

package pg;

public class Main {
public static void main(String[] args) {
char [] f={'皆','大','欢','喜'};
char sec='*';
for(int i=0;i<f.length;i++){
f[i]=(char)(f[i]^sec);
}
for(int i=0;i<f.length;i++){
System.out.printf("%c ", f[i]);
}
System.out.println("");
for(int i=0;i<f.length;i++){
f[i]=(char)(f[i]^sec);
}
for(int i=0;i<f.length;i++){
System.out.printf("%c ", f[i]);
}
}
}


output:


皬 复 欈 営


皆 大 欢 喜





扯远了,回到本问题上来:可以用二进制生成格雷码,然后输出格雷码对应的子集。


#include <cstdio>
#include <iostream>
using namespace std;
int p[25],top;
void show(int x){
top=0;
while(x){
p[top++]=x%2;
x>>=1;
}
for(int i=top-1;i>=0;i--) cout<<p[i];
cout<<endl;
}
int main()
{
int n,t;
while(cin>>n){
for(int i=0;i<(1<<n);i++){
t=i^(i>>1);
//show(t);
top=0;
for(int j=0;j<n;j++){
if(t&(1<<j)){
p[top++]=j+1;
}
}
for(int j=0;j<top;j++){
printf("%3d",p[j]);
}
puts("");
}
}
return 0;
}