以32位无符号整型为例。
1: int Count(unsigned x) {
2: x = x - ((x >> 1) & 0x55555555);
3: x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
4: x = (x + (x >> 4)) & 0x0F0F0F0F;
5: x = x + (x >> 8);
6: x = x + (x >> 16);
7: return x & 0x0000003F;
8: }
这里用的是二分法,两两一组相加,之后四个四个一组相加,接着八个八个,最后就得到各位之和了。
还有一个更巧妙的HAKMEM算法
1: int Count(unsigned x) {
2: unsigned n;
3:
4: n = (x >> 1) & 033333333333;
5: x = x - n;
6: n = (n >> 1) & 033333333333;
7: x = x - n;
8: x = (x + (x >> 3)) & 030707070707;
9: x = modu(x, 63);
10: return x;
11: }
首先是将二进制各位三个一组,求出每组中1的个数,然后相邻两组归并,得到六个一组的1的个数,最后很巧妙的用除63取余得到了结果。
因为2^6 = 64,也就是说 x_0 + x_1 * 64 + x_2 * 64 * 64 = x_0 + x_1 + x_2 (mod 63),这里的等号表示同余。
主定理(master theorem)
T(n)=aT(n/b)+cn
a<b, T(n)=O(n);
a=b, T(n)=O(nlogn);
a>b, T(n)=O(nlogba)
//b为底 logba 为n的次数
你懂得,你不懂,我也没办法。
--create table Oders (OdersID int identity(1,1) primary key, UsersID int)
--create table OderIterms( OdersID int,ProductID int)
--create table Product(ProductID int identity(1,1) primary key , ProductName varchar(50) )
--create table Users(UsersID int identity(1,1) primary key,gender bit,age int )
--declare @i int
--set @i=0
----
--while @i< 10000
--begin
--insert Users select case when rand()>0.5 then 1 else 0 end as gender,CAST( rand()*100 as int) as age
--set @i=@i+1
--end
----
----
--set @i=0
--while @i<10*10000
--begin
--insert Product select '产品'+ CAST( rand()*100 as varchar) as ProductName
--set @i=@i+1
--end
----
----
--set @i=0
--while @i<100*10000
--begin
--insert Oders select top 1 UsersID as UsersID from Users order by NEWID()
--set @i=@i+1
--end
--
declare @i int
set @i=0
declare @maxUser int
select @maxUser= MAX(UsersID) from Users
select CAST( RAND()*@maxUser as int)
declare @minUser int
select @minUser=Min(UsersID) from Users
declare @maxOders int
select @maxOders= MAX(OdersID) from Oders
declare @minOders int
select @minOders= Min(OdersID) from Oders
while @i<1000*10000
begin
insert OderIterms select
@minUser+ CAST(RAND()*@maxUser as int) as OdersID ,
@minOders + CAST(RAND()*@maxOders as int) as OdersID
set @i=@i+1
end
--select COUNT(*) from dbo.OderIterms
--delete from dbo.Users
--delete from dbo.OderIterms
--delete from dbo.Oders
--delete from dbo.Product
--select COUNT(*) from Product
--十万商品
declare @PID int
set @PID=12345
select top 10 A.productID,COUNT(A.productID) 数量
from dbo.OderIterms A
inner join dbo.Oders B
on A.productID!=@PID
and B.UsersID in
(select C.Usersid from dbo.Oders C inner join OderIterms D
on C.OdersID=D.OdersID where D.productID=@PID)
group by A.productID
order by 数量 desc
select A.productID productID ,B.UsersID UsersID into #tb from
OderIterms A inner join Oders B
on A.OdersID=B.OdersID
select top 1 A.productID,B.productID,count(*) from #tb A inner join #tb B
on A.UsersID=B.UsersID and A.productID>B.productID
group by A.productID,B.productID
order by count(*)
O(1)的时间,根据前一组合枚举下一组合
using System;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
combian(5, 3);
Console.Read();
}
static void combian(int n, int k)
{
if (!(n >= k && k > 0 && n < 30))
{
return;
}
int ik = (1 << k) - 1;
while (ik != 0)
{
// 输出组合
for (int i = 0; i < n; ++i)
{
if ((ik & (1 << i)) != 0)
{
Console.Write(i);
}
}
//输出空行分隔之
Console.WriteLine();
ik = next_combination(n, ik);
}
}
static int next_combination(int n, int k)
{
int ret, b = k & -k, t = (k + b);
ret = (((t ^ k) >> 2) / b) | t;
if ((1 << ret) < k)
{
return 0;
}
else
{
return ret;
}
}
}
}
根据k,得到比k大,并且二进制下有相同个数的’1′,而且是当中最小的数
比如把1011,变成1101
首先,因为1的个数要相同,并且比原数要大,那么,先要找出右起第一次出现1的位置,对这个数加上1,然后在最右边补上少了的1就可以了。
找出右起第一次出现1的位置的算法很简单,就是(n & -n),这里的b就是得到这个位置,t就是加了以后的结果,这个应该不难明白,关键的,是后面的计算。
后面的计算,主要是针对右边补1的个数,细心想一下,你就知道,要补的1的个数,等于原数右起第一个1向左数,连续的1的个数减1,然后,t^k是什么意思呢?这个就非常有技巧了,它的效果其实和x ^ (x – 1)很类似。
而x ^ (x – 1)的作用,是保留右起第一个“1”,同时把右起第1个“1”右边全部变为“1”,类似1101000 -> 1111
逆向过来说,就是k = 1100111,对它 +1后,得到t = 1101000,用这个运算可以得到1111,位数和少掉的1成常数差的关系
事实上,这样我们变相地得到少掉的1的个数(这个例子中是少了两个1),我们只需要对运算结果中1的个数减2即可,用>>2解决之
不过,在当k最右边不是1,有若干个0的时候,前一个步骤得到的数的最右边,就会有同样多的0,如何去掉这些0?
这时候,我们最初计算的b,即(n & -n)就太有作用了,只要除以这个b,0就没有了,最后,就是最右边应该补上的值,和前面的t求和,或者求并均可。
输出结果是:
0 1 2
0 1 3
0 2 3
1 2 3
0 1 4
0 2 4
1 2 4
0 3 4
1 3 4
2 3 4
using System;
using System.Linq;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static Random Rand = new Random();
static void Main(string[] args)
{
int count = 10000;
List<int> Input = new List<int>();
for (int i = 0; i < count; i++)
{
Input.Add(Rand.Next(int.MinValue, int.MaxValue));
}
ulong re = PigeonNest(Input, ulong.MaxValue);
Console.WriteLine(re);
Console.WriteLine("-------------");
Console.Read();
}
//鸽巢原理。
static ulong PigeonNest(List<int> List, ulong MinResult)
{
switch (List.Count)
{
case 0:
case 1:
return MinResult;
case 2:
return ABS(List[0], List[1]);
default:
break;
}
int min = List.Min();
//确定桶的大小。
int width = (int)Math.Ceiling((double)(List.Max() - min) / List.Count);
//不可能比1还小了。
if (width == 1) { return 1ul; }
//把数据丢到桶里。
Dictionary<int, NumbersInfo> EachNestNum = new Dictionary<int, NumbersInfo>();
foreach (int n in List)
{
int Key = Convert.ToInt32(Math.Ceiling((double)(n - min) / width));
if (!EachNestNum.ContainsKey(Key))
{
EachNestNum.Add(Key, new NumbersInfo(Key));
}
EachNestNum[Key].Add(n);
}
//找到所有桶里,和相邻两桶的最大最小值距离,三个数中最近的。
foreach (int Key in EachNestNum.Keys)
{
MinResult = Min(MinResult, EachNestNum[Key].minresult(EachNestNum, MinResult));
}
return MinResult;
}
class NumbersInfo
{
public NumbersInfo(int k)
{ key = k; }
private List<int> List = new List<int>();
private int key;
public int max = int.MinValue;
public int min = int.MaxValue;
public int count { get { return List.Count; } }
public ulong minresult(Dictionary<int, NumbersInfo> EachNestNum, ulong re)
{
//在三个数中选最小的。
//当命中数大于1的时候,递归这个过程。由于迅速收敛,故复杂度忽略不计。
if (List.Count > 1)
{
re = PigeonNest(List, re);
}
if (EachNestNum.ContainsKey(key - 1))
{
re = Min(ABS(EachNestNum[key].min, EachNestNum[key - 1].max), re);
}
if (EachNestNum.ContainsKey(key + 1))
{
re = Min(ABS(EachNestNum[key].max, EachNestNum[key + 1].min), re);
}
return re;
}
public void Add(int x)
{
List.Add(x);
if (x > max) { max = x; }
if (x < min) { min = x; }
}
}
static ulong ABS(int x, int y)
{
//三分。
switch (x.CompareTo(y))
{
case -1:
return (ulong)y - (ulong)x;
case 1:
return (ulong)x - (ulong)y;
}
return 0ul;
}
static ulong Min(ulong x, ulong y)
{
if (x > y) { return y; }
return x;
}
}
}
25匹赛马,5个跑道,也就是说每次有5匹马可以同时比赛。问最少比赛多少次可以知道跑得最快的5匹马?
