
目录
所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来标记某个数据在或不在,它解决不了哪个数据出现次数最多的问题。
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?

开一个位图,使用哈希的直接定址法,值是几,就把位图中的比特位标记成1,仅占用512M空间。
但我们不能按照比特位来开辟空间,所以使用char或int等内置类型进行空间的开辟:

仿写bitset:
#pragma once
#include <iostream>
#include <vector>
namespace jly
{
template <size_t N>
class bitset
{
public:
bitset()
{
_bits.resize(N / 8 + 1, 0);
}
public:
void set(size_t x)//将某个比特位标记为1
{
size_t i = x / 8;//算出x位于哪个字节
size_t j = x % 8;//算出x位于该字节的哪一位
_bits[i] |= (1 << j);
}
void reset(size_t x)//将某个比特位标记为0
{
size_t i = x / 8;
size_t j = x % 8;
_bits[i] &= (~(1 << j));
}
bool test(size_t x)//测试这个值在不在位图中
{
size_t i = x / 8;
size_t j = x % 8;
return _bits[i] & (1 << j);
}
private:
std::vector<char> _bits;
};
void test_bitset()
{
bitset<-1> bs;
}
}
这是一种又快又省空间的办法,也是面试官最想听到的回答。
但个人认为如果将题目要求的40亿数字全部录入位图中,等于遍历了一遍40亿个数字,既然都遍历一遍原数据了,那还不如在遍历的时候直接比对呢,对吧,相比之下直接比对数据连512M的位图都不用开。
1、给定100亿个整数,设计算法找到只出现一次的整数?

可以认为这里的整数的最大值为unsigned int的最大值,一个整数共有三种状态:00,01,02,分别代表不存在,出现一次,出现两次及以上,代码如下:
template <size_t N>
class twobitset
{
public:
void set(size_t x)
{
if (_bs1.test(x))//01->10
{
_bs1.reset(x);
_bs2.set(x);
}
else if (_bs1.test(x)==false&&_bs2.test(x)==false)//00->01
{
_bs1.set(x);
}
}
void PrintOnce()
{
for (size_t i = 0; i < N; ++i)
{
if (_bs1.test(i) && !_bs2.test(i))
{
std::cout << i << std::endl;
}
}
}
private:
std::bitset<N> _bs1;
std::bitset<N> _bs2;
};
void test()
{
twobitset<100> tbs;
int a[] = { 3,5,6,3,5,8,9,4,3,6,9,4 };
for (auto& e : a)
{
tbs.set(e);
}
tbs.PrintOnce();//打印8
}
2、给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
和第一问类似,开两个位图,分别将两组数据映射进位图,两个位图对应的比特位均为1即为交集。
3、位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
同第一问,开两个位图,00代表不存在,01代表出现一次,10代表出现两次,11代表出现两次以上。
优点:节省空间,查找速度快
缺点:要求范围相对集中,范围特别分散的,空间消耗大;位图只对整型使用,浮点数、string等其他类型无法使用。
如果要判断其他类型,该类型如果可以使用哈希函数转为整型的,可以考虑下布隆过滤器哈(见下文布隆过滤器的介绍)。
给一个超过100G大小的log fifile, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

如果Ai冲突桶超过1G怎么办?
1、这个桶冲突的IP很多,大多都是不重复的,map统计不下;
2、这个桶冲突的IP很多,大多都是重复的,map可以统计;
直接使用map中的insert将每一个冲突桶的元素插入到map中。情况一:如果insert插入失败,说明空间不足,new节点失败,抛出异常。解决方法是换个哈希函数,递归再次对这个冲突桶进行切分。情况二:map可以正常统计。
布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询。它是用多个哈希函数,将一个数据映射到位图结构中,可以用来告诉你 “某样东西一定不存在或者可能存在”。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。

既然这种方法能判断某个元素一定不存在,那么如何降低“误判”(映射为1的概率)的概率,提升准确判定(映射为0的概率)的概率呢?

解决方法就是对同一个元素使用多组哈希函数进行映射,它能降低误判率,但是增加了空间消耗。使用时需要把控好布隆过滤器的哈希函数的个数和布隆过滤器的长度。公式为【k:哈希函数的个数;m:布隆过滤器的长度;n:插入元素的个数】(选型可参照本文)
1、不需要一定准确的场景,例如个人网站注册时候的昵称判重,使用布隆过滤器可以判断某个昵称一定没有被使用过,但会误判某些造成冲突但没有被使用的昵称。
2、提高效率。例如客户端查找信息时,先用布隆过滤器筛一下,如果不在,则直接将未查到的信息反馈给客户端;如果布隆过滤器发现查找信息与位图匹配,则将需要查找的信息推送给服务器中的数据库进行精确查找。

单纯的布隆过滤器是不支持删除的,因为一个比特位可能被多个元素所映射。如果非要在布隆过滤器中实现reset,那就只能将位图结构修改为计数器结构。数据set时,每被映射一次,计数器加1,reset时,该位计数器-1,直到该位计数器为0。毫无疑问,这种操作所需的空间消耗急剧增加。
优点:
缺点:
给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
近似算法:使用布隆过滤器,先将其中一个文件set进布隆过滤器中,再将另一个文件的数据进行比对,可以淘汰一定不是交集的那部分,不过余下的那部分数据中,仍会有非交集的存在。
精确算法:使用哈希切分,将两个大文件分别切成一个个小文件A0-A99,B0-B99(单个小文件超过1G参照上文哈希切分对于此问题的解决方法);因为使用的是相同的哈希函数,所以交集必定存在于A0和B0,A1和B1这种相同下标的小文件中。可以先将A0存放至哈希表中,B0去重后与哈希表比对,就能够精确得到交集。
#pragma once
#include <iostream>
#include <bitset>
#include <string>
using namespace std;
struct BKDRHash
{
size_t operator()(const std::string& key)
{
size_t hash = 0;
for (auto& ch : key)
{
hash *= 131;
hash += ch;
}
return hash;
}
};
struct APHash
{
size_t operator()(const std::string& key)
{
unsigned int hash = 0;
int i = 0;
for (auto ch : key)
{
if ((i & 1) == 0)
{
hash ^= ((hash << 7) ^ (ch) ^ (hash >> 3));
}
else
{
hash ^= (~((hash << 11) ^ (ch) ^ (hash >> 5)));
}
++i;
}
return hash;
}
};
struct DJBHash
{
size_t operator()(const std::string& key)
{
unsigned int hash = 5381;
for (auto ch : key)
{
hash += (hash << 5) + ch;
}
return hash;
}
};
struct JSHash
{
size_t operator()(const std::string& s)
{
size_t hash = 1315423911;
for (auto ch : s)
{
hash ^= ((hash << 5) + ch + (hash >> 2));
}
return hash;
}
};
//N为最大存储的个数,X为存一个值,需要开辟的比特位
template <size_t N,size_t X=5,class K=std::string,
class HashFunc1= BKDRHash,
class HashFunc2= APHash,
class HashFunc3= DJBHash>
class BloomFilter
{
public:
void set(const K& key)
{
size_t hash1 = HashFunc1()(key) % (X * N);
size_t hash2 = HashFunc2()(key) % (X * N);
size_t hash3 = HashFunc3()(key) % (X * N);
_bs.set(hash1);
_bs.set(hash2);
_bs.set(hash3);
}
bool test(const K& key)
{
size_t hash1 = HashFunc1()(key) % (X * N);
size_t hash2 = HashFunc2()(key) % (X * N);
size_t hash3 = HashFunc3()(key) % (X * N);
return _bs.test(hash1) && _bs.test(hash2) && _bs.test(hash3);
}
private:
std::bitset<X*N> _bs;
};
void test_bloomfilter1()
{
// 10:46继续
string str[] = { "a", "s", "d", "w", "a1","1a","白1a","c11a","1a1" };
BloomFilter<10> bf;
for (auto& str : str)
{
bf.set(str);
}
for (auto& s : str)
{
cout << bf.test(s) << endl;
}
cout << endl;
srand((unsigned int)time(0));
for (const auto& s : str)
{
cout << bf.test(s + to_string(rand())) << endl;
}
}
void test_bloomfilter2()
{
srand((unsigned int)time(0));
const size_t N = 100000;
BloomFilter<N> bf;
std::vector<std::string> v1;
std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
for (size_t i = 0; i < N; ++i)
{
v1.push_back(url + std::to_string(i));
}
for (auto& str : v1)
{
bf.set(str);
}
// v2跟v1是相似字符串集,但是不一样
std::vector<std::string> v2;
for (size_t i = 0; i < N; ++i)
{
std::string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
url += std::to_string(999999 + i);
v2.push_back(url);
}
size_t n2 = 0;
for (auto& str : v2)
{
if (bf.test(str))
{
++n2;
}
}
cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;
// 不相似字符串集
std::vector<std::string> v3;
for (size_t i = 0; i < N; ++i)
{
string url = "zhihu.com";
url += std::to_string(i + rand());
v3.push_back(url);
}
size_t n3 = 0;
for (auto& str : v3)
{
if (bf.test(str))
{
++n3;
}
}
cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
} 对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat
我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>
查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用
是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s