• 关于

    高速存储系统什么意思

    的搜索结果

回答

大数据就是多,就是多。原来的设备存不下、算不动。 ——啪菠萝·毕加索 大数据,不是随机样本,而是所有数据;不是精确性,而是混杂性;不是因果关系,而是相关关系。—— Schönberger 顾名思义“大数据”,从字面意思来理解就是“大量的数据”。 从技术的的角度来解释,大数据就是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 IBM提出大数据具有5V特点:Volume(大量)、Velocity(高速)、Variety(多样)、Value(低价值密度)、Veracity(真实性)。 我们所谈论的大数据实际上更多是从应用的层面,比如某公司搜集、整理了大量的用户行为信息,然后通过数据分析手段对这些信息进行分析从而得出对公司有利用价值的结果。 比如:头条的推荐机制,就是建立在对海量用户的阅读信息的搜集、分析之上。这就是大数据在现实中具体体现。 那Hadoop又是什么?它和大数据又有什么联系呢? Hadoop是一个对海量数据进行处理的分布式系统架构,可以理解为Hadoop就是一个对大量的数据进行分析的工具,和其他组件搭配使用,来完成对大量数据的收集、存储和计算。 Hadoop框架最核心的设计就是:HDFS 和 MapReduce。 HDFS为海量的数据提供了存储;MapReduce为海量的数据提供了计算。 一套完整的Hadoop大数据生态系统基本包含这些组件。 HDFS:Hadoop分布式文件系统,专门存储超大数据文件,为整个Hadoop生态圈提供了基础的存储服务。 MapReduce:分布式离线计算框架,用来处理大量的已经存储在本地的离线数据。 Storm:分布式实时计算,主要特点是实时性,用来处理实时产生的数据。 ZooKeeper:用于Hadoop的分布式协调服务。Hadoop的许多组件依赖于Zookeeper,它运行在计算机集群上面,用于管理Hadoop操作。 HBase:是一个建立在HDFS之上,面向列的NoSQL数据库,用于快速读/写大量数据。 Hive:基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表。 Sqoop:是一个连接工具,用于在关系数据库、数据仓库和Hadoop之间转移数据。 Pig:它是MapReduce编程的复杂性的抽象。Pig平台包括运行环境和用于分析Hadoop数据集的脚本语言(Pig Latin)。

1748847708358317 2019-12-02 03:11:07 0 浏览量 回答数 0

回答

tl; dr:您可能应该使用一维方法。 注意:在不填充书本的情况下比较动态1d或动态2d存储模式时,无法深入研究影响性能的细节,因为代码的性能取决于很多参数。如有可能,进行配置文件。 1.什么更快? 对于密集矩阵,一维方法可能更快,因为它提供了更好的内存局部性以及更少的分配和释放开销。 2.较小的是? 与2D方法相比,Dynamic-1D消耗的内存更少。后者还需要更多分配。 备注 我出于以下几个原因给出了一个很长的答案,但我想首先对您的假设做一些评论。 我可以想象,重新计算1D数组(y + x * n)的索引可能比使用2D数组(x,y)慢 让我们比较这两个函数: int get_2d (int **p, int r, int c) { return p[r][c]; } int get_1d (int *p, int r, int c) { return p[c + C*r]; } Visual Studio 2015 RC为这些功能(启用了优化功能)生成的(非内联)程序集是: ?get_1d@@YAHPAHII@Z PROC push ebp mov ebp, esp mov eax, DWORD PTR _c$[ebp] lea eax, DWORD PTR [eax+edx*4] mov eax, DWORD PTR [ecx+eax*4] pop ebp ret 0 ?get_2d@@YAHPAPAHII@Z PROC push ebp mov ebp, esp mov ecx, DWORD PTR [ecx+edx*4] mov eax, DWORD PTR _c$[ebp] mov eax, DWORD PTR [ecx+eax*4] pop ebp ret 0 区别是mov(2d)与lea(1d)。前者的延迟为3个周期,最大吞吐量为每个周期2个,而后者的延迟为2个周期,最大吞吐量为每个周期3个。(根据指令表-Agner Fog, 由于差异很小,我认为索引重新计算不会产生很大的性能差异。我希望几乎不可能将这种差异本身确定为任何程序的瓶颈。 这将我们带到下一个(也是更有趣的)点: ...但是我可以想象一维可能在CPU缓存中... 是的,但是2d也可能在CPU缓存中。有关为什么1d仍然更好的说明,请参见缺点:内存局部性。 长答案,或者为什么对于简单 /小的矩阵,动态二维数据存储(指针到指针或向量矢量)是“不好的” 。 注意:这是关于动态数组/分配方案[malloc / new / vector等]。静态2D数组是一个连续的内存块,因此不受我将在此处介绍的不利影响。 问题 为了能够理解为什么动态数组的动态数组或向量的矢量最有可能不是选择的数据存储模式,您需要了解此类结构的内存布局。 使用指针语法的示例案例 int main (void) { // allocate memory for 4x4 integers; quick & dirty int ** p = new int*[4]; for (size_t i=0; i<4; ++i) p[i] = new int[4]; // do some stuff here, using p[x][y] // deallocate memory for (size_t i=0; i<4; ++i) delete[] p[i]; delete[] p; } 缺点 内存位置 对于此“矩阵”,您分配一个包含四个指针的块和四个包含四个整数的块。所有分配都不相关,因此可以导致任意存储位置。 下图将使您了解内存的外观。 对于真正的二维情况: 紫色正方形是其p自身占据的存储位置。 绿色方块将存储区域p点组装为(4 x int*)。 4个连续的蓝色方块的4个区域是每个int*绿色区域所指向的区域 对于在1d情况下映射的2d: 绿色方块是唯一需要的指针 int * 蓝色方块组合了所有矩阵元素的存储区域(16 x int)。 实际2D与映射2D内存布局 这意味着(例如,使用左侧布局时)(例如,使用缓存),与连续存储模式(如右侧所示)相比,您可能会发现性能较差。 假设高速缓存行是“一次传输到高速缓存中的数据量”,并想象一个程序一个接一个地访问整个矩阵。 如果您具有正确对齐的32位值的4 4矩阵,则具有64字节高速缓存行(典型值)的处理器能够“一次性”读取数据(4 * 4 * 4 = 64字节)。如果您开始处理而缓存中还没有数据,则将面临缓存未命中,并且将从主内存中获取数据。由于且仅当连续存储(并正确对齐)时,此负载才能装入整个缓存行,因此可以立即读取整个矩阵。处理该数据时可能不会再有任何遗漏。 在动态的“真实二维”系统中,每行/列的位置都不相关,处理器需要分别加载每个内存位置。即使只需要64个字节,在最坏的情况下,为4个不相关的内存位置加载4条高速缓存行实际上会传输256个字节并浪费75%的吞吐量带宽。如果使用2d方案处理数据,您将再次在第一个元素上遇到缓存未命中(如果尚未缓存)。但是现在,从主内存中第一次加载后,只有第一行/列会在缓存中,因为所有其他行都位于内存中的其他位置,并且不与第一行/列相邻。一旦到达新的行/列,就会再次出现高速缓存未命中,并从主内存执行下一次加载。 长话短说:2d模式具有较高的缓存未命中率,而1d方案由于数据的局部性而具有更好的性能潜力。 频繁分配/取消分配 N + 1创建所需的NxM(4×4)矩阵需要多达(4 + 1 = 5)个分配(使用new,malloc,allocator :: allocate或其他方法)。 也必须应用相同数量的适当的各自的重新分配操作。 因此,与单个分配方案相比,创建/复制此类矩阵的成本更高。 随着行数的增加,情况变得更加糟糕。 内存消耗开销 我假设int的大小为32位,指针的大小为32位。(注意:系统依赖性。) 让我们记住:我们要存储一个4×4 int矩阵,表示64个字节。 对于NxM矩阵,使用提出的指针对指针方案存储,我们消耗了 NMsizeof(int) [实际的蓝色数据] + Nsizeof(int) [绿色指针] + sizeof(int**) [紫罗兰色变量p]字节。 444 + 44 + 4 = 84在本示例的情况下,这会使字节变多,使用时甚至会变得更糟std::vector<std::vector >。对于4 x 4 int ,它将需要N * M * sizeof(int)+ N * sizeof(vector )+ sizeof(vector<vector >)字节,即4 44 + 416 + 16 = 144总共字节,共64个字节。 另外-根据所使用的分配器-每个单独的分配可能(并且很可能会)还有16个字节的内存开销。(一些“信息字节”用于存储已分配的字节数,以进行适当的重新分配。) 这意味着最坏的情况是: N*(16+Msizeof(int)) + 16+Nsizeof(int*) + sizeof(int**) = 4*(16+44) + 16+44 + 4 = 164 bytes ! Overhead: 156% 开销的份额将随着矩阵大小的增加而减少,但仍然存在。 内存泄漏的风险 一堆分配需要适当的异常处理,以避免在其中一个分配失败的情况下发生内存泄漏!您需要跟踪分配的内存块,并且在释放内存时一定不要忘记它们。 如果new无法运行内存并且无法分配下一行(特别是在矩阵很大时),std::bad_alloc则抛出a new。 例: 在上面提到的new / delete示例中,如果要避免发生bad_alloc异常时的泄漏,我们将面临更多代码。 // allocate memory for 4x4 integers; quick & dirty size_t const N = 4; // we don't need try for this allocation // if it fails there is no leak int ** p = new int*[N]; size_t allocs(0U); try { // try block doing further allocations for (size_t i=0; i<N; ++i) { p[i] = new int[4]; // allocate ++allocs; // advance counter if no exception occured } } catch (std::bad_alloc & be) { // if an exception occurs we need to free out memory for (size_t i=0; i<allocs; ++i) delete[] p[i]; // free all alloced p[i]s delete[] p; // free p throw; // rethrow bad_alloc } /* do some stuff here, using p[x][y] */ // deallocate memory accoding to the number of allocations for (size_t i=0; i<allocs; ++i) delete[] p[i]; delete[] p; 摘要 在某些情况下,“真实的2d”内存布局适合并且有意义(即,如果每行的列数不是恒定的),但是在最简单和常见的2D数据存储情况下,它们只会使代码的复杂性膨胀,并降低性能和程序的内存效率。 另类 您应该使用连续的内存块,并将行映射到该内存块。 做到这一点的“ C ++方式”可能是编写一个类来管理您的内存,同时考虑诸如 什么是三法则? 资源获取是什么意思初始化(RAII)? C ++概念:容器(在cppreference.com上) 例 为了提供这样一个类的外观的想法,下面是一个具有一些基本功能的简单示例: 二维尺寸可构造 2d可调整大小 operator(size_t, size_t) 用于2行主要元素访问 at(size_t, size_t) 用于检查的第二行主要元素访问 满足容器的概念要求 资源: #include #include #include #include namespace matrices { template class simple { public: // misc types using data_type = std::vector ; using value_type = typename std::vector ::value_type; using size_type = typename std::vector ::size_type; // ref using reference = typename std::vector ::reference; using const_reference = typename std::vector ::const_reference; // iter using iterator = typename std::vector ::iterator; using const_iterator = typename std::vector ::const_iterator; // reverse iter using reverse_iterator = typename std::vector ::reverse_iterator; using const_reverse_iterator = typename std::vector ::const_reverse_iterator; // empty construction simple() = default; // default-insert rows*cols values simple(size_type rows, size_type cols) : m_rows(rows), m_cols(cols), m_data(rows*cols) {} // copy initialized matrix rows*cols simple(size_type rows, size_type cols, const_reference val) : m_rows(rows), m_cols(cols), m_data(rows*cols, val) {} // 1d-iterators iterator begin() { return m_data.begin(); } iterator end() { return m_data.end(); } const_iterator begin() const { return m_data.begin(); } const_iterator end() const { return m_data.end(); } const_iterator cbegin() const { return m_data.cbegin(); } const_iterator cend() const { return m_data.cend(); } reverse_iterator rbegin() { return m_data.rbegin(); } reverse_iterator rend() { return m_data.rend(); } const_reverse_iterator rbegin() const { return m_data.rbegin(); } const_reverse_iterator rend() const { return m_data.rend(); } const_reverse_iterator crbegin() const { return m_data.crbegin(); } const_reverse_iterator crend() const { return m_data.crend(); } // element access (row major indexation) reference operator() (size_type const row, size_type const column) { return m_data[m_cols*row + column]; } const_reference operator() (size_type const row, size_type const column) const { return m_data[m_cols*row + column]; } reference at() (size_type const row, size_type const column) { return m_data.at(m_cols*row + column); } const_reference at() (size_type const row, size_type const column) const { return m_data.at(m_cols*row + column); } // resizing void resize(size_type new_rows, size_type new_cols) { // new matrix new_rows times new_cols simple tmp(new_rows, new_cols); // select smaller row and col size auto mc = std::min(m_cols, new_cols); auto mr = std::min(m_rows, new_rows); for (size_type i(0U); i < mr; ++i) { // iterators to begin of rows auto row = begin() + i*m_cols; auto tmp_row = tmp.begin() + i*new_cols; // move mc elements to tmp std::move(row, row + mc, tmp_row); } // move assignment to this *this = std::move(tmp); } // size and capacity size_type size() const { return m_data.size(); } size_type max_size() const { return m_data.max_size(); } bool empty() const { return m_data.empty(); } // dimensionality size_type rows() const { return m_rows; } size_type cols() const { return m_cols; } // data swapping void swap(simple &rhs) { using std::swap; m_data.swap(rhs.m_data); swap(m_rows, rhs.m_rows); swap(m_cols, rhs.m_cols); } private: // content size_type m_rows{ 0u }; size_type m_cols{ 0u }; data_type m_data{}; }; template void swap(simple & lhs, simple & rhs) { lhs.swap(rhs); } template bool operator== (simple const &a, simple const &b) { if (a.rows() != b.rows() || a.cols() != b.cols()) { return false; } return std::equal(a.begin(), a.end(), b.begin(), b.end()); } template bool operator!= (simple const &a, simple const &b) { return !(a == b); } } 请注意以下几点: T需要满足使用的std::vector成员函数的要求 operator() 不执行任何“范围”检查 无需自己管理数据 不需要析构函数,复制构造函数或赋值运算符 因此,您不必费心为每个应用程序进行适当的内存处理,而只需为编写的类一次即可。 限制条件 在某些情况下,动态“真实”二维结构是有利的。例如,如果 矩阵非常大且稀疏(如果甚至不需要分配任何行,但可以使用nullptr对其进行处理),或者 这些行没有相同数量的列(也就是说,如果您根本没有矩阵,而只有另一个二维结构)。

保持可爱mmm 2020-02-09 13:47:55 0 浏览量 回答数 0

回答

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。 [编辑本段]基本概念 * 若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个思想建立的表为散列表。 * 对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称冲突。具有相同函数值的关键字对该散列函数来说称做同义词。综上所述,根据散列函数H(key)和处理冲突的方法将一组关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象” 作为记录在表中的存储位置,这种表便称为散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。 * 若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。 [编辑本段]常用的构造散列函数的方法 散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位ǐ 1. 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a•key + b,其中a和b为常数(这种散列函数叫做自身函数) 2. 数字分析法 3. 平方取中法 4. 折叠法 5. 随机数法 6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p, p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。 [编辑本段]处理冲突的方法 1. 开放寻址法:Hi=(H(key) + di) MOD m, i=1,2,…, k(k<=m-1),其中H(key)为散列函数,m为散列表长,di为增量序列,可有下列三种取法: 1. di=1,2,3,…, m-1,称线性探测再散列; 2. di=1^2, (-1)^2, 2^2,(-2)^2, (3)^2, …, ±(k)^2,(k<=m/2)称二次探测再散列; 3. di=伪随机数序列,称伪随机探测再散列。 == 2. 再散列法:Hi=RHi(key), i=1,2,…,k RHi均是不同的散列函数,即在同义词产生地址冲突时计算另一个散列函数地址,直到冲突不再发生,这种方法不易产生“聚集”,但增加了计算时间。 3. 链地址法(拉链法) 4. 建立一个公共溢出区 [编辑本段]查找的性能分析 散列表的查找过程基本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到,另一些关键码在散列函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理冲突的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对散列表查找效率的量度,依然用平均查找长度来衡量。 查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。因此,影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素: 1. 散列函数是否均匀; 2. 处理冲突的方法; 3. 散列表的装填因子。 散列表的装填因子定义为:α= 填入表中的元素个数 / 散列表的长度 α是散列表装满程度的标志因子。由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,填入表中的元素较多,产生冲突的可能性就越大;α越小,填入表中的元素较少,产生冲突的可能性就越小。 实际上,散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数。 了解了hash基本定义,就不能不提到一些著名的hash算法,MD5 和 SHA-1 可以说是目前应用最广泛的Hash算法,而它们都是以 MD4 为基础设计的。那么他们都是什么意思呢? 这里简单说一下: (1) MD4 MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年设计的,MD 是 Message Digest 的缩写。它适用在32位字长的处理器上用高速软件实现--它是基于 32 位操作数的位操作来实现的。 (2) MD5 MD5(RFC 1321)是 Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好 (3) SHA-1 及其他 SHA1是由NIST NSA设计为同DSA一起使用的,它对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。SHA-1 设计时基于和MD4相同原理,并且模仿了该算法。 那么这些Hash算法到底有什么用呢? Hash算法在信息安全方面的应用主要体现在以下的3个方面: (1) 文件校验 我们比较熟悉的校验算法有奇偶校验和CRC校验,这2种校验并没有抗数据篡改的能力,它们一定程度上能检测并纠正数据传输中的信道误码,但却不能防止对数据的恶意破坏。 MD5 Hash算法的"数字指纹"特性,使它成为目前应用最广泛的一种文件完整性校验和(Checksum)算法,不少Unix系统有提供计算md5 checksum的命令。 (2) 数字签名 Hash 算法也是现代密码体系中的一个重要组成部分。由于非对称算法的运算速度较慢,所以在数字签名协议中,单向散列函数扮演了一个重要的角色。 对 Hash 值,又称"数字摘要"进行数字签名,在统计上可以认为与对文件本身进行数字签名是等效的。而且这样的协议还有其他的优点。 (3) 鉴权协议 如下的鉴权协议又被称作挑战--认证模式:在传输信道是可被侦听,但不可被篡改的情况下,这是一种简单而安全的方法。 MD5、SHA1的破解 2004年8月17日,在美国加州圣芭芭拉召开的国际密码大会上,山东大学王小云教授在国际会议上首次宣布了她及她的研究小组近年来的研究成果——对MD5、HAVAL-128、MD4和RIPEMD等四个著名密码算法的破译结果。 次年二月宣布破解SHA-1密码。 [编辑本段]实际应用 以上就是一些关于hash以及其相关的一些基本预备知识。那么在emule里面他具体起到什么作用呢? 大家都知道emule是基于P2P (Peer-to-peer的缩写,指的是点对点的意思的软件), 它采用了"多源文件传输协议”(MFTP,the Multisource FileTransfer Protocol)。在协议中,定义了一系列传输、压缩和打包还有积分的标准,emule 对于每个文件都有md5-hash的算法设置,这使得该文件独一无二,并且在整个网络上都可以追踪得到。 什么是文件的hash值呢? MD5-Hash-文件的数字文摘通过Hash函数计算得到。不管文件长度如何,它的Hash函数计算结果是一个固定长度的数字。与加密算法不同,这一个Hash算法是一个不可逆的单向函数。采用安全性高的Hash算法,如MD5、SHA时,两个不同的文件几乎不可能得到相同的Hash结果。因此,一旦文件被修改,就可检测出来。 当我们的文件放到emule里面进行共享发布的时候,emule会根据hash算法自动生成这个文件的hash值,他就是这个文件唯一的身份标志,它包含了这个文件的基本信息,然后把它提交到所连接的服务器。当有他人想对这个文件提出下载请求的时候, 这个hash值可以让他人知道他正在下载的文件是不是就是他所想要的。尤其是在文件的其他属性被更改之后(如名称等)这个值就更显得重要。而且服务器还提供了,这个文件当前所在的用户的地址,端口等信息,这样emule就知道到哪里去下载了。 一般来讲我们要搜索一个文件,emule在得到了这个信息后,会向被添加的服务器发出请求,要求得到有相同hash值的文件。而服务器则返回持有这个文件的用户信息。这样我们的客户端就可以直接的和拥有那个文件的用户沟通,看看是不是可以从他那里下载所需的文件。 对于emule中文件的hash值是固定的,也是唯一的,它就相当于这个文件的信息摘要,无论这个文件在谁的机器上,他的hash值都是不变的,无论过了多长时间,这个值始终如一,当我们在进行文件的下载上传过程中,emule都是通过这个值来确定文件。 那么什么是userhash呢? 道理同上,当我们在第一次使用emule的时候,emule会自动生成一个值,这个值也是唯一的,它是我们在emule世界里面的标志,只要你不卸载,不删除config,你的userhash值也就永远不变,积分制度就是通过这个值在起作用,emule里面的积分保存,身份识别,都是使用这个值,而和你的id和你的用户名无关,你随便怎么改这些东西,你的userhash值都是不变的,这也充分保证了公平性。其实他也是一个信息摘要,只不过保存的不是文件信息,而是我们每个人的信息。 那么什么是hash文件呢? 我们经常在emule日志里面看到,emule正在hash文件,这里就是利用了hash算法的文件校验性这个功能了,文章前面已经说了一些这些功能,其实这部分是一个非常复杂的过程,目前在ftp,bt等软件里面都是用的这个基本原理,emule里面是采用文件分块传输,这样传输的每一块都要进行对比校验,如果错误则要进行重新下载,这期间这些相关信息写入met文件,直到整个任务完成,这个时候part文件进行重新命名,然后使用move命令,把它传送到incoming文件里面,然后met文件自动删除,所以我们有的时候会遇到hash文件失败,就是指的是met里面的信息出了错误不能够和part文件匹配,另外有的时候开机也要疯狂hash,有两种情况一种是你在第一次使用,这个时候要hash提取所有文件信息,还有一种情况就是上一次你非法关机,那么这个时候就是要进行排错校验了。 关于hash的算法研究,一直是信息科学里面的一个前沿,尤其在网络技术普及的今天,他的重要性越来越突出,其实我们每天在网上进行的信息交流安全验证,我们在使用的操作系统密钥原理,里面都有它的身影,特别对于那些研究信息安全有兴趣的朋友,这更是一个打开信息世界的钥匙,他在hack世界里面也是一个研究的焦点。 一般的线性表、树中,记录在结构中的相对位置是随机的即和记录的关键字之间不存在确定的关系,在结构中查找记录时需进行一系列和关键字的比较。这一类查找方法建立在“比较”的基础上,查找的效率与比较次数密切相关。理想的情况是能直接找到需要的记录,因此必须在记录的存储位置和它的关键字之间建立一确定的对应关系f,使每个关键字和结构中一个唯一的存储位置相对应。因而查找时,只需根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上,由此不需要进行比较便可直接取得所查记录。在此,称这个对应关系f为哈希函数,按这个思想建立的表为哈希表(又称为杂凑法或散列表)。 哈希表不可避免冲突(collision)现象:对不同的关键字可能得到同一哈希地址 即key1≠key2,而hash(key1)=hash(key2)。具有相同函数值的关键字对该哈希函数来说称为同义词(synonym)。 因此,在建造哈希表时不仅要设定一个好的哈希函数,而且要设定一种处理冲突的方法。可如下描述哈希表:根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映象到一个有限的、地址连续的地址集(区间)上并以关键字在地址集中的“象”作为相应记录在表中的存储位置,这种表被称为哈希表。 对于动态查找表而言,1) 表长不确定;2)在设计查找表时,只知道关键字所属范围,而不知道确切的关键字。因此,一般情况需建立一个函数关系,以f(key)作为关键字为key的录在表中的位置,通常称这个函数f(key)为哈希函数。(注意:这个函数并不一定是数学函数) 哈希函数是一个映象,即:将关键字的集合映射到某个地址集合上,它的设置很灵活,只要这个地址集合的大小不超出允许范围即可。 现实中哈希函数是需要构造的,并且构造的好才能使用的好。 用途:加密,解决冲突问题。。。。 用途很广,比特精灵中就使用了哈希函数,你可 以自己看看。 具体可以学习一下数据结构和算法的书。 [编辑本段]字符串哈希函数 (著名的ELFhash算法) int ELFhash(char *key) return h%MOD; }

晚来风急 2019-12-02 01:22:24 0 浏览量 回答数 0

阿里云爆款特惠专场,精选爆款产品低至0.95折!

爆款ECS云服务器8.1元/月起,云数据库低至1.5折,限时抢购!
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站