Hash算法总结

1. Hash是什么,它的作用

先举个例子。我们每个活在世上的人,为了能够参与各种社会活动,都需要一个用于识别自己的标志。也许你觉得名字或是身份证就足以代表你这个人,但是这种代表性非常脆弱,因为重名的人很多,身份证也可以伪造。最可靠的办法是把一个人的所有基因序列记录下来用来代表这个人,但显然,这样做并不实际。而指纹看上去是一种不错的选择,虽然一些专业组织仍然可以模拟某个人的指纹,但这种代价实在太高了。

而对于在互联网世界里传送的文件来说,如何标志一个文件的身份同样重要。比如说我们下载一个文件,文件的下载过程中会经过很多网络服务器、路由器的中转,如何保证这个文件就是我们所需要的呢?我们不可能去一一检测这个文件的每个字节,也不能简单地利用文件名、文件大小这些极容易伪装的信息,这时候,我们就需要一种指纹一样的标志来检查文件的可靠性,这种指纹就是我们现在所用的Hash算法(也叫散列算法)。

散列算法(Hash Algorithm),又称哈希算法,杂凑算法,是一种从任意文件中创造小的数字「指纹」的方法。与指纹一样,散列算法就是一种以较短的信息来保证文件唯一性的标志,这种标志与文件的每一个字节都相关,而且难以找到逆向规律。因此,当原有文件发生改变时,其标志值也会发生改变,从而告诉文件使用者当前的文件已经不是你所需求的文件。

这种标志有何意义呢?之前文件下载过程就是一个很好的例子,事实上,现在大部分的网络部署和版本控制工具都在使用散列算法来保证文件可靠性。而另一方面,我们在进行文件系统同步、备份等工具时,使用散列算法来标志文件唯一性能帮助我们减少系统开销,这一点在很多云存储服务器中都有应用。

以Git为代表的众多版本控制工具都在使用SHA1等散列函数检查文件更新

当然,作为一种指纹,散列算法最重要的用途在于给证书、文档、密码等高安全系数的内容添加加密保护。这一方面的用途主要是得益于散列算法的不可逆性,这种不可逆性体现在,你不仅不可能根据一段通过散列算法得到的指纹来获得原有的文件,也不可能简单地创造一个文件并让它的指纹与一段目标指纹相一致。散列算法的这种不可逆性维持着很多安全框架的运营,而这也将是本文讨论的重点。

2. Hash算法有什么特点

一个优秀的 hash 算法,将能实现:

  • 正向快速:给定明文和 hash 算法,在有限时间和有限资源内能计算出 hash 值。
  • 逆向困难:给定(若干) hash 值,在有限时间内很难(基本不可能)逆推出明文。
  • 输入敏感:原始输入信息修改一点信息,产生的 hash 值看起来应该都有很大不同。
  • 冲突避免:很难找到两段内容不同的明文,使得它们的 hash 值一致(发生冲突)。即对于任意两个不同的数据块,其hash值相同的可能性极小;对于一个给定的数据块,找到和它hash值相同的数据块极为困难。

但在不同的使用场景中,如数据结构和安全领域里,其中对某一些特点会有所侧重。

2.1 Hash在管理数据结构中的应用

在用到hash进行管理的数据结构中,就对速度比较重视,对抗碰撞不太看中,只要保证hash均匀分布就可以。比如hashmap,hash值(key)存在的目的是加速键值对的查找,key的作用是为了将元素适当地放在各个桶里,对于抗碰撞的要求没有那么高。换句话说,hash出来的key,只要保证value大致均匀的放在不同的桶里就可以了。但整个算法的set性能,直接与hash值产生的速度有关,所以这时候的hash值的产生速度就尤为重要,以JDK中的String.hashCode()方法为例:

public int hashCode() {int h = hash;//hash default value : 0 if (h == 0 && value.length > 0) {//value : char storagechar val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;
}

很简洁的一个乘加迭代运算,在不少的hash算法中,使用的是异或+加法进行迭代,速度和前者差不多。

2.1 Hash在在密码学中的应用

在密码学中,hash算法的作用主要是用于消息摘要和签名,换句话说,它主要用于对整个消息的完整性进行校验。举个例子,我们登陆知乎的时候都需要输入密码,那么知乎如果明文保存这个密码,那么黑客就很容易窃取大家的密码来登陆,特别不安全。那么知乎就想到了一个方法,使用hash算法生成一个密码的签名,知乎后台只保存这个签名值。由于hash算法是不可逆的,那么黑客即便得到这个签名,也丝毫没有用处;而如果你在网站登陆界面上输入你的密码,那么知乎后台就会重新计算一下这个hash值,与网站中储存的原hash值进行比对,如果相同,证明你拥有这个账户的密码,那么就会允许你登陆。银行也是如此,银行是万万不敢保存用户密码的原文的,只会保存密码的hash值而而已。在这些应用场景里,对于抗碰撞和抗篡改能力要求极高,对速度的要求在其次。一个设计良好的hash算法,其抗碰撞能力是很高的。以MD5为例,其输出长度为128位,设计预期碰撞概率为2^128,这是一个极小极小的数字——而即便是在MD5被王小云教授破解之后,其碰撞概率上限也高达,也就是说,至少需要找次才能有1/2的概率来找到一个与目标文件相同的hash值。而对于两个相似的字符串,MD5加密结果如下:

MD5("version1") = "966634ebf2fc135707d6753692bf4b1e";
MD5("version2") = "2e0e95285f08a07dea17e7ee111b21c8";

可以看到仅仅一个比特位的改变,二者的MD5值就天差地别了

ps : 其实把hash算法当成是一种加密算法,这是不准确的,我们知道加密总是相对于解密而言的,没有解密何谈加密呢,HASH的设计以无法解为目的的。并且如果我们不附加一个随机的salt值,HASH口令是很容易被字典攻击入侵的。

3. Hash算法是如何实现的?

密码学和信息安全发展到现在,各种加密算法和散列算法已经不是只言片语所能解释得了的。在这里我们仅提供几个简单的概念供大家参考。

作为散列算法,首要的功能就是要使用一种算法把原有的体积很大的文件信息用若干个字符来记录,还要保证每一个字节都会对最终结果产生影响。那么大家也许已经想到了,求模这种算法就能满足我们的需要。

事实上,求模算法作为一种不可逆的计算方法,已经成为了整个现代密码学的根基。只要是涉及到计算机安全和加密的领域,都会有模计算的身影。散列算法也并不例外,一种最原始的散列算法就是单纯地选择一个数进行模运算,比如以下程序。

#  构造散列函数
def hash(a):return a % 8#  测试散列函数功能
print(hash(233))
print(hash(234))
print(hash(235))# 输出结果
- 1
- 2
- 3

很显然,上述的程序完成了一个散列算法所应当实现的初级目标:用较少的文本量代表很长的内容(求模之后的数字肯定小于8)。但也许你已经注意到了,单纯使用求模算法计算之后的结果带有明显的规律性,这种规律将导致算法将能难保证不可逆性。所以我们将使用另外一种手段,那就是异或。

再来看下面一段程序,我们在散列函数中加入一个异或过程。

#  构造散列函数
def hash(a):return (a % 8) ^ 5#  测试散列函数功能
print(hash(233))
print(hash(234))
print(hash(235))# 输出结果
- 4
- 7
- 6

很明显的,加入一层异或过程之后,计算之后的结果规律性就不是那么明显了。

当然,大家也许会觉得这样的算法依旧很不安全,如果用户使用连续变化的一系列文本与计算结果相比对,就很有可能找到算法所包含的规律。但是我们还有其他的办法。比如在进行计算之前对原始文本进行修改,或是加入额外的运算过程(如移位),比如以下程序。

#  构造散列函数
def hash(a):return (a + 2 + (a << 1)) % 8 ^ 5#  测试散列函数功能
print(hash(233))
print(hash(234))
print(hash(235))# 输出结果
- 0
- 5
- 6

这样处理得到的散列算法就很难发现其内部规律,也就是说,我们并不能很轻易地给出一个数,让它经过上述散列函数运算之后的结果等于4——除非我们去穷举测试。

上面的算法是不是很简单?事实上,下面我们即将介绍的常用算法MD5和SHA1,其本质算法就是这么简单,只不过会加入更多的循环和计算,来加强散列函数的可靠性。

4. Hash有哪些流行的算法

目前流行的 Hash 算法包括 MD5、SHA-1 和 SHA-2。

  • MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年设计的,MD 是 Message Digest 的缩写。其输出为 128 位。MD4 已证明不够安全。

  • MD5(RFC 1321)是 Rivest 于1991年对 MD4 的改进版本。它对输入仍以 512 位分组,其输出是 128 位。MD5 比 MD4 复杂,并且计算速度要慢一点,更安全一些。MD5 已被证明不具备"强抗碰撞性"。

  • SHA (Secure Hash Algorithm)是一个 Hash 函数族,由 NIST(National Institute of Standards and Technology)于 1993 年发布第一个算法。目前知名的 SHA-1 在 1995 年面世,它的输出为长度 160 位的 hash 值,因此抗穷举性更好。SHA-1 设计时基于和 MD4 相同原理,并且模仿了该算法。SHA-1 已被证明不具"强抗碰撞性"。

  • 为了提高安全性,NIST 还设计出了 SHA-224、SHA-256、SHA-384,和 SHA-512 算法(统称为 SHA-2),跟 SHA-1 算法原理类似。SHA-3 相关算法也已被提出。

可以看出,上面这几种流行的算法,它们最重要的一点区别就是"强抗碰撞性"。

5. 那么,何谓Hash算法的「碰撞」?

你可能已经发现了,在实现算法章节的第一个例子,我们尝试的散列算法得到的值一定是一个不大于8的自然数,因此,如果我们随便拿9个数去计算,肯定至少会得到两个相同的值,我们把这种情况就叫做散列算法的「碰撞」(Collision)。

这很容易理解,因为作为一种可用的散列算法,其位数一定是有限的,也就是说它能记录的文件是有限的——而文件数量是无限的,两个文件指纹发生碰撞的概率永远不会是零。

但这并不意味着散列算法就不能用了,因为凡事都要考虑代价,买光所有彩票去中一次头奖是毫无意义的。现代散列算法所存在的理由就是,它的不可逆性能在较大概率上得到实现,也就是说,发现碰撞的概率很小,这种碰撞能被利用的概率更小。

随意找到一组碰撞是有可能的,只要穷举就可以。散列算法得到的指纹位数是有限的,比如MD5算法指纹字长为128位,意味着只要我们穷举2^128次,就肯定能得到一组碰撞——当然,这个时间代价是难以想象的,而更重要的是,仅仅找到一组碰撞并没有什么实际意义。更有意义的是,如果我们已经有了一组指纹,能否找到一个原始文件,让它的散列计算结果等于这组指纹。如果这一点被实现,我们就可以很容易地篡改和伪造网络证书、密码等关键信息。

你也许已经听过MD5已经被破解的新闻——但事实上,即便是MD5这种已经过时的散列算法,也很难实现逆向运算。我们现在更多的还是依赖于海量字典来进行尝试,也就是通过已经知道的大量的文件——指纹对应关系,搜索某个指纹所对应的文件是否在数据库里存在。

5.1 MD5的实际碰撞案例

下面让我们来看看一个真实的碰撞案例。我们之所以说MD5过时,是因为它在某些时候已经很难表现出散列算法的某些优势——比如在应对文件的微小修改时,散列算法得到的指纹结果应当有显著的不同,而下面的程序说明了MD5并不能实现这一点。

import hashlib#  两段HEX字节串,注意它们有细微差别
a = bytearray.fromhex("0e306561559aa787d00bc6f70bbdfe3404cf03659e704f8534c00ffb659c4c8740cc942feb2da115a3f4155cbb8607497386656d7d1f34a42059d78f5a8dd1ef")b = bytearray.fromhex("0e306561559aa787d00bc6f70bbdfe3404cf03659e744f8534c00ffb659c4c8740cc942feb2da115a3f415dcbb8607497386656d7d1f34a42059d78f5a8dd1ef")#  输出MD5,它们的结果一致
print(hashlib.md5(a).hexdigest())
print(hashlib.md5(b).hexdigest())### a和b输出结果都为:
cee9a457e790cf20d4bdaa6d69f01e41
cee9a457e790cf20d4bdaa6d69f01e41

而诸如此类的碰撞案例还有很多,上面只是原始文件相对较小的一个例子。事实上现在我们用智能手机只要数秒就能找到MD5的一个碰撞案例,因此,MD5在数年前就已经不被推荐作为应用中的散列算法方案,取代它的是SHA家族算法,也就是安全散列算法(Secure Hash Algorithm,缩写为SHA)。

5.2 SHA家族算法以及SHA1碰撞

安全散列算法与MD5算法本质上的算法是类似的,但安全性要领先很多——这种领先型更多的表现在碰撞攻击的时间开销更大,当然相对应的计算时间也会慢一点。

SHA家族算法的种类很多,有SHA0、SHA1、SHA256、SHA384等等,它们的计算方式和计算速度都有差别。其中SHA1是现在用途最广泛的一种算法。包括GitHub在内的众多版本控制工具以及各种云同步服务都是用SHA1来区别文件,很多安全证书或是签名也使用SHA1来保证唯一性。长期以来,人们都认为SHA1是十分安全的,至少大家还没有找到一次碰撞案例。

但这一事实在2017年2月破灭了。CWI和Google的研究人员们成功找到了一例SHA1碰撞,而且很厉害的是,发生碰撞的是两个真实的、可阅读的PDF文件。这两个PDF文件内容不相同,但SHA1值完全一样。(对于这件事的影响范围及讨论,可参考知乎上的讨论:如何评价 2 月 23 日谷歌宣布实现了 SHA-1 碰撞?)

所以,对于一些大的商业机构来说, MD5 和 SHA1 已经不够安全,推荐至少使用 SHA2-256 算法。

6. Hash在Java中的应用

6.1 HashMap的复杂度

在介绍HashMap的实现之前,先考虑一下,HashMap与ArrayList和LinkedList在数据复杂度上有什么区别。下图是他们的性能对比图:

获取查找添加/删除空间
ArrayListO(1)O(1)O(N)O(N)
LinkedListO(N)O(N)O(1)O(N)
HashMapO(N/Bucket_size)O(N/Bucket_size)O(N/Bucket_size)O(N)

可以看出HashMap整体上性能都非常不错,但是不稳定,为O(N/Buckets),N就是以数组中没有发生碰撞的元素,Buckets是因碰撞产生的链表。

注:发生碰撞实际上是非常稀少的,所以N/Bucket_size约等于1

HashMap是对Array与Link的折衷处理,Array与Link可以说是两个速度方向的极端,Array注重于数据的获取,而处理修改(添加/删除)的效率非常低;Link由于是每个对象都保持着下一个对象的指针,查找某个数据需要遍历之前所有的数据,所以效率比较低,而在修改操作中比较快。

6.2 HashMap的实现

本文以JDK8的API实现进行分析

6.2.1 对key进行Hash计算

在JDK8中,由于使用了红黑树来处理大的链表开销,所以hash这边可以更加省力了,只用计算hashCode并移动到低位就可以了。

static final int hash(Object key) {int h;//计算hashCode,并无符号移动到低位return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

举个例子: 363771819^(363771819 >>> 16)

0001 0101 1010 1110 1011 0111 1010 1011(363771819)
0000 0000 0000 0000 0001 0101 1010 1110(5550) XOR
--------------------------------------- =
0001 0101 1010 1110 1010 0010 0000 0101(363766277)

这样做可以实现了高地位更加均匀地混到一起。

下面给出在Java中几个常用的哈希码(hashCode)的算法。

  1. Object类的hashCode. 返回对象的经过处理后的内存地址,由于每个对象的内存地址都不一样,所以哈希码也不一样。这个是native方法,取决于JVM的内部设计,一般是某种C地址的偏移。

  2. String类的hashCode. 根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串的内容相同,返回的哈希码也相同。

  3. Integer等包装类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100), i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。

  4. int,char这样的基础类,它们不需要hashCode,如果需要存储时,将进行自动装箱操作,计算方法同上。

6.2.2 获取到数组的index的位置

计算了Hash,我们现在要把它插入数组中了

i = (tab.length - 1) & hash;

通过位运算,确定了当前的位置,因为HashMap数组的大小总是2^n,所以实际的运算就是 (0xfff...ff) & hash ,这里的tab.length-1相当于一个mask,滤掉了大于当前长度位的hash,使每个i都能插入到数组中。

6.2.3 生成包装类

这个对象是一个包装类,Node<K,V>,内部有key,value,hash还有next,可以看出来它是一个链表。

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;//getter and setter .etc.
}

6.2.4 插入包装类到数组

(1). 如果输入当前的位置是空的,就插进去,如图,左为插入前,右为插入后

    0           0|           |1 -> null   1 - > null|           |2 -> null   2 - > null|           | ..-> null   ..- > null|           | i -> null   i - > new node|           |n -> null   n - > null

(2). 如果当前位置已经有了node,且它们发生了碰撞,则新的放到前面,旧的放到后面,这叫做链地址法处理冲突。

    0           0|           |1 -> null   1 - > null|           |2 -> null   2 - > null|           | ..-> null   ..- > null|           | i -> old    i - > new - > old|           |n -> null   n - > null

我们可以发现,失败的hashCode算法会导致HashMap的性能由数组下降为链表,所以想要避免发生碰撞,就要提高hashCode结果的均匀性。

6.3 扩容

如果当表中的75%已经被占用,即视为需要扩容了

(threshold = capacity * load factor ) < size

它主要有两个步骤:

6.3.1 容量加倍

左移1位,就是扩大到两倍,用位运算取代了乘法运算

newCap = oldCap << 1;
newThr = oldThr << 1;

6.3.2 遍历计算Hash

for (int j = 0; j < oldCap; ++j) {Node<K,V> e;//如果发现当前有Bucketif ((e = oldTab[j]) != null) {oldTab[j] = null;//如果这里没有碰撞if (e.next == null)//重新计算Hash,分配位置newTab[e.hash & (newCap - 1)] = e;//这个见下面的新特性介绍,如果是树,就填入树else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//如果是链表,就保留顺序....目前就看懂这点else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}

由此可以看出扩容需要遍历并重新赋值,成本非常高,所以选择一个好的初始容量非常重要。

6.4 扩容如何提升性能?

  • 解决扩容损失:如果知道大致需要的容量,把初始容量设置好以解决扩容损失;
    比如我现在有1000个数据,需要 1000/0.75 = 1333 个坑位,又 1024 < 1333 < 2048,所以最好使用2048作为初始容量。

  • 解决碰撞损失:使用高效的HashCode与loadFactor,这个...由于JDK8的高性能出现,这儿问题也不大了。

6.5 HashMap与HashTable的主要区别

在很多的Java基础书上都已经说过了,他们的主要区别其实就是Table全局加了线程同步保护

  • HashTable线程更加安全,代价就是因为它粗暴的添加了同步锁,所以会有性能损失。
  • 其实有更好的concurrentHashMap可以替代HashTable,一个是方法级,一个是Class级。

6.6 在Android中使用SparseArray代替HashMap

官方推荐使用SparseArray([spɑ:s][ə'reɪ],稀疏的数组)或者LongSparseArray代替HashMap。官方总结有一下几点好处:

  • SparseArray使用基本类型(Primitive)中的int作为Key,不需要Pair<K,V>或者Entry<K,V>这样的包装类,节约了内存;

  • SpareArray维护的是一个排序好的数组,使用二分查找数据,即O(log(N)),每次插入数据都要进行排序,同样耗时O(N);而HashMap使用hashCode来加入/查找/删除数据,即O(N/buckets_size);

  • 总的来说,就是SparseArray针对Android嵌入式设备进行了优化,牺牲了微小的时间性能,换取了更大的内存优化;同时它还有别的优化,比如对删除操作做了优化;

  • 如果你的数据非常少(实际上也是如此),那么使用SpareArray也是不错的;

总结

「The Algorithm Design Manual」一书中提到,雅虎的 Chief Scientist ,Udi Manber 曾说过,在 yahoo 所应用的算法中,最重要的三个是:Hash,Hash 和 Hash。其实从上文中所举的git用sha1判断文件更改,密码用MD5生成摘要后加盐等等对Hash的应用可看出,Hash的在计算机世界扮演着多么重要的角色。另书中还举了一个很有趣的显示中例子:

一场拍卖会中,物品是价高者得,如果每个人只有一次出价机会,同时提交自己的价格后,最后一起公布,出价最高则胜出。这种形式存在作弊的可能,如果有出价者能 hack 进后台,然后将自己的价格改为最高价 +1,则能以最低的代价获得胜利。如何杜绝这种作弊呢?

答案很简单,参与者都提交自身出价的 hash 值就可以了,即使有人能黑进后台也无法得知明文价格,等到公布之时,再对比原出价与 hash 值是否对应即可。是不是很巧妙?

是的,上面的做法,与上文提到的网站上储存密码用MD5 值而非明文,是同一种思想,殊途同归。

可以看到无论是密码学、数据结构、现实生活中的应用,到处可以看到Hash的影子,通过这篇文章的介绍,相信你不仅知其名,也能懂其意。

Reference

     转自:    https://www.jianshu.com/p/bf1d7eee28d0

  1. https://jizhi.im/blog/post/sha1decrypt
  2. http://www.jianshu.com/p/e54047b2b563
  3. https://www.zhihu.com/question/26762707
  4. http://mp.weixin.qq.com/s?__biz=MzA5ODUzOTA0OQ==&mid=2651688220&idx=1&sn=a3f9cb1e186ffe22d9825bca00e85c76&chksm=8b692e5abc1ea74ce61a819f5666dd7d73ee45d6145c92b993de271a315d4f3d3fb3874f9be3&mpshare=1&scene=23&srcid=0414EOLCuLSu17uo8Aw8refB#rd
  5. http://mp.weixin.qq.com/s/oRLkR7jplqO2qhHtUeTMIA





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/44532.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Randao 可证公平随机数(VRF)白皮书

Randao 可证公平随机数白皮书 randao.org September 11, 2017 Abstract Randao 基于区块链技术&#xff0c;提供开源的、去中心化的、社交化的、可证公平的随机数生成服务。Randao 继承了常用随机数发生器的不可控制性及不可预测性&#xff0c;同时具备其所不具有的可参与性及可…

区块链之零知识证明(zk-SNARK从小白到明白)

零知识证明&#xff1a;从小白到明白 如今&#xff0c;知识快餐业发达&#xff0c;区块链这么火的领域自然不会落下。经过一轮轮扫盲&#xff0c;共识、工作量证明、闪电网络等等概念对普罗大众已不再陌生&#xff0c;甚至各种解构、比喻、引申&#xff0c;将术语炒得比本义还…

谈一谈今年的移动互联网寒冬

转载请注明出处&#xff1a;http://blog.csdn.net/guolin_blog/article/details/52316072 本文同步发表于我的微信公众号&#xff0c;扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注&#xff0c;每天都有文章更新。 大家好&#xff0c;好像有段时间没写技术文章了。主要是最…

区块链:7 个步骤入门区块链

2017 年是比特币大爆发的一年。在潜水这么多年之后&#xff0c;比特币的价格一下子从 1000 美元左右飞涨至将近 20000 美元。多少人摇身一变&#xff0c;成了“密码学货币交易专家”。 赶上好时候确实可以狠赚一笔&#xff0c;不过醒醒吧&#xff0c;盛宴已散。 -2013 年 10 月…

如何用 Java 实现简单的区块链

点击上方蓝色“程序猿DD”&#xff0c;选择“设为星标” 回复“资源”获取独家整理的学习资料&#xff01; 来源 | 公众号「锅外的大佬」 1. 概述 本文中&#xff0c;我们将学习区块链技术的基本概念。也将根据概念使用 Java 来实现一个基本的应用程序。 进一步&#xff0c;我…

国字号遥感算法大赛!涵盖主流视觉任务,头奖10万人民币!

点击我爱计算机视觉标星&#xff0c;更快获取CVML新技术 大赛背景 本届遥感图像稀疏表征与智能分析竞赛由国家自然科学基金委信息科学部、“空间信息网络基础理论与关键技术”重大研究计划指导专家组主办&#xff0c;旨在推动“空间信息网络基础理论与关键技术”重大研究计划科…

js 模拟超级大LE透中头奖 统计中头奖需要购买的彩票次数以及购买总金额

<!DOCTYPE html> <html> <head><title>超级大LE透模拟</title> </head><div id"container">超级大LE透<br>规则&#xff1a;超级大LE透基本投注是指从前区号码中任选5个号码&#xff0c;并从后区号码中任选2个号码的…

挖挖双色球——数据挖掘技术 分享

最近双色球比较热闹&#xff0c;因为河南1彩民独中双色球3.6亿巨奖&#xff01; 《媒体报道&#xff1a;2009年10月8日&#xff0c;国庆长假结束前的最后一天&#xff0c;在这个注定要被写进中国彩票史的日子&#xff0c;河南省安阳市成为了全国瞩目的焦点。当期中国福利彩票“…

成都程序员双色球中2682万!号码是电脑算出来的?

你人生中最狂野的梦想是什么&#xff1f; 如果是播妞来回答的话&#xff0c;播妞做梦都想中一次彩票头奖&#xff0c;彻底解放自己被贫穷限制的想象力&#xff0c;来一次不一样的人生&#xff01; 播妞是没有这么幸运了&#xff0c;不过&#xff0c;近日&#xff0c;四川成都的…

Python网络爬虫和信息提取:(动态网站)双色球数据爬取及写入数据库Sqlite、json和Excel表

我想着拿什么练习下网络爬虫信息提取时&#xff0c;就想到了双色球&#xff0c;心想把往期数据提取出来也是个不错的主意&#xff0c;把数据保存下来以后做数据分析&#xff0c;根据分析结果去买双色球岂不是美哉&#xff1f;&#xff01;哈哈哈。。 当然这里仅是爬取和保存&am…

双色球

最近身边很多人玩双色球。。我也买了几期。 下面是双色球模拟程序&#xff0c;代码如下&#xff1a; package Test;import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList;…

双色球的概率

为什么80%的码农都做不了架构师&#xff1f;>>> 双色球所有的组合数&#xff1a;33x32x31x30x29x28/6/5/4/3/2/1x1617721088。因此随机买一注的话&#xff0c;中头奖的概率约为“一千七百七十二万分之一”。最近一期双色球为15028期&#xff0c;销售额为318 360 50…

java 随机生成双色球

1.描述 输入注数&#xff0c;Java随机生成双色球 2.代码 import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.Scanner;public class Two_color_ball {// 随机生成双色球 前区 01-33 取6个数 后区…

诗歌与词曲收藏

《和子由渑池怀旧》-&#xff08;宋&#xff09;苏轼 人生到处知何似&#xff0c;应似飞鸿踏雪泥。 泥上偶然留指爪&#xff0c;鸿飞那复计东西。 老僧已死成新塔&#xff0c;坏壁无由见旧题。 往日崎岖还记否&#xff0c;路长人困蹇驴嘶。 《年轻的心》-席慕容 不再回头的 …

【情人节表白神器:送她一个HTML动态表白网站 带源码】

ChatGPT 介绍 ChatGPT 是由 OpenAI 开发的高级语言模型。它是一种基于变换器的神经网络&#xff0c;已经在互联网上的大量文本数据上进行了训练。这使得 ChatGPT 具有很强的语言理解能力&#xff0c;能够生成人类般的文本内容&#xff0c;如回答问题、生成摘要、翻译等。 近期…

基础知识6

知乎上的面试题&#xff1a;https://zhuanlan.zhihu.com/p/546032003 一、Topk问题以及变种&#xff0c;各种解法 微博的热门排行就属于 TopK 问题 TopK 一般是要求在 N 个数的集合中找到最小或者最大的 K 个值&#xff0c;通常 N 都非常得大。 算法的优点是不用在内存中读入全…

美云智数孔凡实:工业软件“突围战”——强化核心技术+渠道共赢生态丨数据猿专访...

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 “仿真和数字孪生是企业数字化发展中的刚需的&#xff0c;不论是什么制造业&#xff0c;都会有产品&#xff0c;有产品就会有制造的仿真。”——美云智数渠道产品BU总经理 孔凡实 随着元宇宙概念的火热&#xff0c;数字孪生…

日语毕业论文日文参考文献怎么找?

要说什么东西能够让一位当代大学生茶饭不思、日渐消瘦&#xff0c;那么论文一定能够排得上号。近些年&#xff0c;学术不端的行为在学术界受到越来越多的关注&#xff0c;对于学位论文的要求也越来越高。大家都知道&#xff0c;写出一篇学位论文很难。写出一篇日语毕业论文更是…

GPT 吞噬一切!我们还需要编程语言吗?

作者 | GPT-4 责编 | 唐门教主 出品 | 《智能之境》&#xff0c;一个由 AIGC 创作的栏目 编者按 「智能之境」专栏更新&#xff1a;AGI 的未来&#xff0c;究竟属于 Rust 还是 Mojo&#xff1f;或者我们还需要编程语言吗&#xff1f; LLVM 之父、苹果的编程语言 Swift 之父、新…

ChatGPT 技术首发上车,集度汽车官宣将融合文心一言;谷歌自研数据中心芯片取得新进展;Firefox 110 发布|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…