发表评论
public class Tow
{
private StringBuilder data;
private int length;
public int Length
{
get
{
return this.length;
}
}
public Tow(int num)
{
if (num < 1)
{
throw new Exception();
}
this.length = num;
this.data = new StringBuilder(this.length);
for (int i = 0; i < this.length; i++)
{
this.data.Append('0');
}
}
public string Grow()
{
for (int index = this.data.Length - 1; index > -1; index--)
{
if (this.data[index] == '0')
{
this.data[index] = '1';
return this.data.ToString();
}
else if (this.data[index] == '1')
{
this.data[index] = '0';
}
else
{
throw new Exception();
}
}
return this.data.ToString();
}
}
class Program
{
static void Main(string[] args)
{
int n = 3;
Tow tow = new Tow(n);
for (int i = 0; i < Math.Pow(2, tow.Length); i++)
{
Console.WriteLine(tow.Grow());
}
Console.WriteLine("OK");
}
}
你的代码很值得商量
我仔细看先
感觉一个N位的转换不需要你这么麻烦
确定一下,这里这个N假设是不大于4字节整型的是不是啊?
#3楼 [
楼主]2007-10-17 13:10 |
@徐少侠
N是一个任意大小的正整数
#4楼 [
楼主]2007-10-17 13:12 |
@徐少侠
用到%和/以后,算法的速度自然会受到影响。
可以比较一下算法的时间复杂度,
具体的可以来一个统计实际时间的时间。
补充一下,这个问题的时间复杂度决定了这个N不能太大。
--引用--------------------------------------------------
riordan: 简单的排列组合问题,有什么好讨论的?
--------------------------------------------------------
有时间就研究贝
标准的排列组合算法在解决这个小问题上显得太大。
程序是程序员创造出来的,标准算法仅用来参考
例如以博主的例子
3位数的计算过程中
其实每次后面的都包含了前面的结果
有什么办法仅做最后的那次运算,从而输出所有结果呢?
速度自然快不少的。
不过要花费点存储空间了
#12楼 [
楼主]2007-10-17 14:31 |
@猪怕壮
这确实是一个好的思路,希望您能给出具体的实现代码^_^
#13楼 [
楼主]2007-10-17 14:32 |
@请输入你的姓名
just do it and test it.
#14楼 [
楼主]2007-10-17 14:33 |
@riordan
咱们讨论的问题确实没有很大的难度。
不过我很有兴趣看到您对这个简单问题的具体解法。
class Program
{
static void Main(string[] args)
{
int n=0;
string [][] arr;
n =20;
//初始化数组
arr = new string[n][];
int k,j;
for (k = 1; k <= n; k++)
{
arr[k-1] = new string[(int)Math.Pow(2,k)];
}
//计算并生成结果字符串
for (k = 0; k < n;k++ )
{
if(k==0)
{
arr[0][0] = "0";
arr[0][1] = "1";
}
else
{
int tmpIndex = k - 1;
int tmpLength = arr[tmpIndex].Length;
for (j = 0; j < tmpLength;j++ )
{
arr[k][j] = "0" + arr[tmpIndex][j];
arr[k][j + tmpLength] = "1" + arr[tmpIndex][j];
}
}
}
//输出结果
for (k = 0; k < n; k++)
{
foreach (string str in arr[k])
{
Console.Write(str);
Console.Write("\t");
}
Console.WriteLine();
Console.WriteLine("----------------------------");
}
Console.Read();
}
}
运行成功
测试时候计算20位的
不考虑屏幕输出的时候,仅需要3秒以下就可以了
但是前提是你的机器内存要大。否则他要清理内存的。
20位需要150兆,虚拟内存157兆
21位需要增加75%的需求
自然,算法还可以改
时间和空间的矛盾这里应该能看出来啦。
--引用--------------------------------------------------
aspnetx: 进行2的n次方循环,把n位数枚举出来
--------------------------------------------------------
关键是要以二进制方式输出
@徐少侠
那这样行不?外围一个循环,为n
然后单独写一个方法是把这个n转换成二进制的,套在循环体中然后做输出?
#21楼 [
楼主]2007-10-17 15:03 |
@徐少侠
呵呵,看了你的算法,非常棒!
我测试了一下,算1到20位一共用了4秒(在我的机器上 T5500 2G RAM)
我测试了我的算法,只算20位的所有表示,1秒以内,21位的所有表示1秒。
--引用--------------------------------------------------
aspnetx: @徐少侠
那这样行不?外围一个循环,为n
然后单独写一个方法是把这个n转换成二进制的,套在循环体中然后做输出?
--------------------------------------------------------
每次每个数字都转换似乎效率不高哦
@徐少侠
呵呵,看怎么用了.其实我也感觉这个方法要"无耻"一些,但是如果n不是大的很离谱的话,不知能否看出效率的差别.
'20次 760ms
'21次 1560ms
[code=vb.net]
Private Function f(ByVal n As Integer) As String
If n < 1 Then
Return String.Empty
End If
Dim str As String = "0 1"
For i As Integer = 2 To n
str = "0" & str.Replace(" ", " 0") & " 1" & str.Replace(" ", " 1")
Next
Return str
End Function
[/code]
数字转成二进制字符串,其实就是Convert.ToString(i, 2),可惜它暴露的方法过少,System内有个很强很完美的方法,可惜又不公开,大家看看优化一下这个程序:
static void Main(string[] args)
{
DateTime t = DateTime.Now;
String s = RendN(20, true);
TimeSpan ts = DateTime.Now - t;
Console.Write(ts.TotalMilliseconds);
Console.WriteLine("");
t = DateTime.Now;
s = RendN(20, false);
ts = DateTime.Now - t;
Console.Write(ts.TotalMilliseconds);
Console.WriteLine("");
//Console.Write(s);
Console.ReadKey();
}
static String RendN(int n, bool flag)
{
if (n < 0) return null;
int k = (int)Math.Pow(2, n);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < k; i++)
{
String ss;
if (flag)
{
ss = Convert.ToString(i, 2);
sb.Append(new String('0', n - ss.Length));
}
else
ss = IntToString(i, n);
sb.Append(ss);
sb.Append(" ");
}
return sb.ToString();
}
static String IntToString(int k, int n)
{
return (String)ParseNum.Invoke(n, new Object[] { k, 2, n, '0', 0 });
}
static MethodInfo _ParseNum;
static MethodInfo ParseNum
{
get
{
if (_ParseNum != null) return _ParseNum;
Assembly ass = Assembly.GetAssembly(typeof(System.Object));
_ParseNum = ass.GetType("System.ParseNumbers").GetMethod("IntToString");
return _ParseNum;
}
}
感情都比一个位数阿
不是从2位开始到N哦?
那继续修改,看看大家能把速度提到多少。
#29楼 [
楼主]2007-10-17 18:02 |
@徐少侠
呵呵,是啊,咱们都只算了一个具体的位数。
#30楼 [
楼主]2007-10-17 18:02 |
@学海无涯,回头是岸
学习!
--引用--------------------------------------------------
学海无涯,回头是岸: '20次 760ms
'21次 1560ms
--------------------------------------------------------
你这个东西好玩
赫赫。写程序的乐趣大致如此了
亲自试了一下,还是蛮难的。不能到32,不然内存不够用呀。
跑21,我这不输出用了0.87,输出用了2.2
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Specialized;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
fun(21);
Console.ReadKey();
}
public static void fun(short n)
{
Stopwatch sw = Stopwatch.StartNew();
uint max = (uint)Math.Pow(2, n);
char[,] result = new char[max, n];
for (uint i = 0; i < max; i++)
{
for (uint j = 0, k = i; j < n; j++)
{
if ((k & 1) != 0)
{
result[i, j] = '1';
}
else
{
result[i, j] = '0';
}
k = k >> 1;
}
}
sw.Stop();
Console.Title = sw.Elapsed.ToString();
sw.Start();
StringBuilder sb = new StringBuilder((int)(max * (n + 1)));
for (uint i = 0; i < max; i++)
{
for (uint j = 0; j < n; j++)
{
sb.Append(result[i, j]);
}
sb.Append('\n');
}
sw.Stop();
Console.Title = Console.Title + "--" + sw.Elapsed.ToString();
Console.WriteLine(sb.ToString());
}
}
}
#33楼 [
楼主]2007-10-17 18:58 |
@大石头
学习!
都碰上内存问题了哦
为了保存结果,就要耗费大量内存了
目前的纪录是多少哦?
我写了一个直接显示的版本,就是说每次计算好后就显示结果
节约内存了
运行时只要十多兆
去除显示语句,速度飞快哦
建议大家不要合并字符串,直接算出某一个子串就可以了,那样不会占用过多资源,我们就可以得到更好的速度了。合并字符串输出,那是IO的问题。
大家可有什么办法提高我那代码中反射的性能?代码上,调用ParseNumber类是最简单的,就一句,但是必须用反射来调用,如果不用反射,性能肯定最好的,因为那个函数是Native实现的^_^
@徐少侠
1 算出2的n次最大是多少
2 从0~2的n次最大数循环加1
3 数转二进制,然后输出二进制形式
今天有空我实验一下速度如何
@大石头
参数True,使用ToString的方式似乎速度快哦
不过还没我自己写的快,哈哈
等下贴上来
代码比较复杂哦,哈哈
@猪怕壮
你的思路和大石头上面的代码一致
我的机器上他不输出也不保存结果,仅运算需要710~750毫秒
public delegate void AddHandler();
//位类
class Bit
{
public event AddHandler overflow;
private bool _num;
public int Numer
{
get
{
if (_num)
{
return 1;
}
else
{
return 0;
}
}
}
public Bit()
{
_num = false;
}
public void Add()
{
_num = !_num;
if(!_num)
{
overflow();
}
}
}
//另外一个类做为排列做准备
class Two
{
private System.Text.StringBuilder outString;
bool f;
private int _length;
public int Length
{
set
{
if (value > 0)
{
_length = value;
}
}
get
{
return _length;
}
}
public Two()
{
outString=new StringBuilder();
_length =1;
f = true;
}
public string m2()
{
Bit[] arr = new Bit[_length];
int i, j;
arr[0] = new Bit();
arr[0].overflow += new AddHandler(Two_overflow);
for (i = 1; i < _length; i++)
{
arr[i] = new Bit();
arr[i].overflow += new AddHandler(arr[i - 1].Add);
}
while (f)
{
for (j = 0; j < _length; j++)
{
//outString.Append(arr[j].Numer);
}
arr[_length - 1].Add();
//outString.Append("\t");
}
return outString.ToString();
}
void Two_overflow()
{
f = false;
}
}
//主程序
class Program
{
static void Main(string[] args)
{
Two t = new Two();
t.Length = 20;
string outs;
TimeSpan ts;
DateTime t1 = DateTime.Now;
outs=t.m2();
ts = DateTime.Now - t1;
Console.WriteLine(ts.TotalMilliseconds);
Console.Read();
}
}
Intel Celeron M370
1500MHZ
512M DDR PC3200
Windows 2003 Server R2
20位171.875
21位359
22位750
23位1500
基本就是倍数增长了
比大石头那个快
因为算法思路不同
搞这样的比较有什么意义?
博客园上没人知道算法复杂度这个东西吗? NP难的问题!
@无知1
请评估一下这里的算法复杂度?
另外,我的代码似乎不是任何一本书上有的
我自己写的
如果我们学算法之后,仅仅是知道世界上有多少算法
就如同我们学习了设计模式后,自己没有能力创造模式那样
不算学好了。
各位,这里的算法复杂度是不能那样算的,基数不一致呀。
在增加两种递归的方法,最快的速度是跑26位耗时4秒
static void Main(string[] args)
{
int Count = 26;
Console.Write("请输入位数:");
Count = int.Parse(Console.ReadLine());
DateTime t = DateTime.Now;
String s;
//s = RendN(Count, true);
TimeSpan ts = DateTime.Now - t;
Console.Write("Convert.ToString:" + ts.TotalMilliseconds);
Console.WriteLine("");
t = DateTime.Now;
//s = RendN(Count - 2, false);
ts = DateTime.Now - t;
Console.Write("IntToString:" + ts.TotalMilliseconds);
Console.WriteLine("");
t = DateTime.Now;
//s = RendM(Count, "");
ts = DateTime.Now - t;
Console.Write("递归一:" + ts.TotalMilliseconds);
Console.WriteLine("");
t = DateTime.Now;
StringBuilder ss = new StringBuilder();
ss.Append(new String('0', Count) + " ");
s = RendM(Count, ss, 1);
ts = DateTime.Now - t;
Console.Write("递归二:" + ts.TotalMilliseconds);
Console.WriteLine("");
//Console.Write(s);
Console.ReadKey();
}
static String RendN(int n, bool flag)
{
if (n < 0) return null;
int k = (int)Math.Pow(2, n);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < k; i++)
{
String ss;
if (flag)
{
ss = Convert.ToString(i, 2);
sb.Append(new String('0', n - ss.Length));
}
else
ss = IntToString(i, n);
sb.Append(ss);
sb.Append(" ");
}
return sb.ToString();
}
static String IntToString(int k, int n)
{
return (String)ParseNum.Invoke(n, new Object[] { k, 2, n, '0', 0 });
}
static MethodInfo _ParseNum;
static MethodInfo ParseNum
{
get
{
if (_ParseNum != null) return _ParseNum;
Assembly ass = Assembly.GetAssembly(typeof(System.Object));
_ParseNum = ass.GetType("System.ParseNumbers").GetMethod("IntToString");
return _ParseNum;
}
}
/// <summary>
/// 计算
/// </summary>
/// <param name="m">位数</param>
/// <param name="head">头</param>
/// <returns></returns>
static String RendM(int m, String head)
{
if (m > 1)
{
RendM(m - 1, head + "0");
RendM(m - 1, head + "1");
return null;
}
String s;
s = head + "0 ";
s = head + "1 ";
return null;
}
/// <summary>
/// 计算
/// </summary>
/// <param name="m">位数</param>
/// <param name="head">头</param>
/// <param name="n">位置</param>
/// <returns></returns>
static String RendM(int m, StringBuilder head, int n)
{
if (m > n)
{
head[n - 1] = '0';
RendM(m, head, n + 1);
head[n - 1] = '1';
RendM(m, head, n + 1);
return null;
}
head[n - 1] = '0';
//Console.Write(head);
head[n - 1] = '1';
//Console.Write(head);
return null;
}
using System;
using System.Collections.Generic;
using System.Text;
namespace TestBoolIncreate
{
class Program
{
static void Main(string[] args)
{
Program _program = new Program();
int howlong = 20;
_program.run(howlong);
}
public void run( int x )
{
DateTime StartTime = DateTime.Now;
Double summer = Math.Pow(2, x);
for (int i = 0; i < summer; i++)
{
string value = ConverToX(2, i);
//Console.WriteLine( value );
}
Console.WriteLine("End");
DateTime EndTime = DateTime.Now;
TimeSpan intervalTimeSpan = EndTime - StartTime ;
Console.WriteLine("用时{0}秒", intervalTimeSpan.TotalSeconds);
Console.ReadLine();
}
/// <summary>
/// 转换数据到几位的string
/// </summary>
/// <param name="num"></param>
/// <param name="bitX"></param>
public string ConverToX(int bitX , int num )
{
string ReturnValue = "";
if (num / bitX == 0 && num % bitX == 0)
{
return "0";
}
while ( ! (num / bitX == 0 && num % bitX == 0))
{
int jieguo = num / bitX;
int yu = num % bitX;
ReturnValue = ReturnValue.Insert(0, yu.ToString());
num = jieguo;
}
return ReturnValue;
}
}
}
不输出结果20位38秒
这回比 徐少侠 那个要快点了。
还有,如果我用双线程计算,理论上耗时是现在的50% ^_^
@徐少侠
你根本就不懂什么是算法复杂度,而不是有没有学好的问题!
其实作者的算法并没错,速度慢的原因有2:
1 循环不应该:
for (int i = 0; i < Math.Pow(2, tow.Length); i++)
要把 Math.Pow(2, tow.Length) 移出来,避免每次求Pow
2 Tow类的data数据类型不应该是StringBuilder,应用char[]
修改后的代码,N = 24 只要0.75秒, 我的机器CPU:2G, 内存:512M
using System;
using System.Collections.Generic;
using System.Text;
namespace AllTuples
{
public class Tow
{
private char[] data;
private int length;
public int Length
{
get
{
return this.length;
}
}
public Tow(int num)
{
if (num < 1)
{
throw new Exception();
}
this.length = num;
this.data = new char[num];
for (int i = 0; i < this.length; i++)
{
this.data[i] = '0';
}
}
public char[] Grow()
{
for (int index = this.data.Length - 1; index > -1; index--)
{
if (this.data[index] == '0')
{
this.data[index] = '1';
return data;
}
else if (this.data[index] == '1')
{
this.data[index] = '0';
}
else
{
throw new Exception();
}
}
return data;
}
}
class Program
{
static void Main(string[] args)
{
int n = 24;
Tow tow = new Tow(n);
int count = (int)Math.Pow(2, tow.Length);
DateTime t1 = DateTime.Now;
for (int i = 0; i < count; i++)
{
tow.Grow();
//Console.WriteLine(tow.Grow());
}
Console.WriteLine(DateTime.Now - t1);
Console.WriteLine("OK");
Console.Read();
}
}
}
原算法还是有一些小问题
当N为3时,如果数一下Grow里的循环次数,会发现循环了14次。
而这样写,只要7次,当N大了以后,就会影响速度了。
public char[] Grow()
{
int pos = length - 1;
while (pos > 0 && data[pos] == '1')
{
data[pos] = '0';
pos--;
}
data[pos] = '1';
return data;
}
同意楼上的,或许我那里的第四种方法把StringBuilder改为char应该快点吧
--引用--------------------------------------------------
大石头: 这回比 徐少侠 那个要快点了。
还有,如果我用双线程计算,理论上耗时是现在的50% ^_^
--------------------------------------------------------
双线程计算
最好是双核
单核的似乎效果不明显
把StringBuilder改成char后,更快了,5秒就可以跑28位了
现在正在写一种新的以空间换时间的算法
#58楼 [
楼主]2007-10-19 22:51 |
@Andy1990zx
哈哈,欢迎你来。
@ji yang
1 循环不应该:
for (int i = 0; i < Math.Pow(2, tow.Length); i++)
要把 Math.Pow(2, tow.Length) 移出来,避免每次求Pow
记得CLR在编译为IL时会自动将for循环中的边界条件缓存起来的,所以不会每次求Pow
#61楼 [
楼主]
2008-06-23 10:28 |
@U2U
呵呵,能说说你的思路吗?