首页 > 编程语言 >区块链底层算法基础:有限群及其代码实现

区块链底层算法基础:有限群及其代码实现

时间:2023-06-14 11:32:28浏览次数:42  
标签:prime __ self 元素 算法 num 区块 other 底层


区块链完全可以说是人类智慧的结晶,它的诞生是人类科技文明发展到一定程度的结果展现。区块链的功能得以实现要有赖于加解密技术的发展,而后者又来源于数论和抽象代数几百年的发展,因此要把握区块链的技术思路,不了解其加解密原理,那你就不可能掌握区块链的技术精髓,所以我们庖丁解牛,一点点的研究和解析相应的算法设计思路,首先需要看的是来自抽象代数的有限群概念。

在抽象代数中,”群“其实是一系列元素的集合,并且在这些元素上定义了两种运算,分别为“+”和“*”, 如果你没有接触过抽象代数,你很容易把这两种运算跟我们熟悉的四则运算中的加法和乘法混淆。一组由有限个元素构成的集合只要能满足以下几个性质就可以形成“群”,
1,任意两个集合中的元素a,b , 如果他们进行“+” 和"*“运算后,所得结果依然属于该集合,也就是a”+“b, 和a”*"b也属于该集合,这个性质叫封闭性。
2,存在一个特定元素,我们称之为0,它与任何元素进行”+“运算后,所得结果就是元素本身,也就是 a “+” 0 = a
3, 存在一个特定元素,我们称之为1,它与任何元素进行”*"运算后,所得结果就是元素本身,也就是 a “*” 1 = a
4, 如果a是集合中的元素,那么集合中存在另一个元素b, 使得 a “+” b = 0, 我们把满足条件的b用符号-a表示
5,如果a是集合中的元素,那么集合存在另一个元素c,使得a “*” c = 1, 我们把满足条件的c称为1/a

我们看具体例子,集合F={0, 1, 2, 3, 4}, 如果我们把定义中的两种运算理解为普通的加法和乘法,我们可以看到F无法满足有限群的要求,因为 5 = 1+4 不属于该集合,同时6 = 2 * 3 也属于该集合。但是我们只要把两种运算稍微做个改变就可以,集合中元素的个数有5个,如果我们做完普通的加法和乘法运算后,将结果再对5进行求余运算,例如 5 % 5 = 0 属于集合F, 6 % 5 = 1属于集合F,于是定义中的两种操作只要先对元素进行普通的加法和乘法运算后再将结果在元素个数的基础上求余即可。

由此我们可以构造特定的有限群F§ = {0, 1, 2, …, p - 1}其中p是素数。前面例子中的群就是F(5),接下来我们使用python代码来定义有限群的元素:

class FieldElemet:
    def __init__(self, num, prime):
        # prime 对应群的大小,也就是元素的数量, num就是元素对应数值
        if num >= prime or num < 0:
            # 群元素必须是大于等于0的整数
            error = f'field element shound be integer in range 0 to {prime-1}'
            raise ValueError(error)
        self.num = num
        self.prime = prime

    def __repr__(self):
        return f'FieldElement with value:{self.num} and order:{self.prime}'

    def __eq__(self, other):
        if other is None:
            return False
        return self.num == other.num and self.prime == other.prime 
       

a = FieldElemet(7, 23)
b = FieldElemet(6, 23)
print(a == b)
print(a == a)

上面代码运行后会输出False 和 True。接下来我们要实现两种有限群操作,也就是加法和乘法,他们实际上就是普通四则运算后再针对群的元素个数进行求余,因此我们要增加__add__的实现:

def __add__(self, other):
        if self.prime != other.prime:
            raise TypeError('there are two different field')
        num = (self.num + other.num) % self.prime
        #新建生成一个对象
        return self.__class__(num, self.prime)
    def __sub__(self, other):
        if self.prime != other.prime:
            raise TypeError('there are two different fields')
        num = (self. num - other.num) % self.prime
        return self.__class__(num, self.prime)
     
     def __mul__(self, other):
        if self.prime != other.prime:
            raise TypeError('there are two different fields')
        num = (self.num * other.num) % self.prime
        return self.__class__(num, self.prime)
    def __pow__(self, num):
        if num < 0:
            raise ValueError(f"exponent should >=0 but value is {num}")
        num = pow(self.num, num, self.prime)
        return self.__class__(num, self.prime)

上面代码同时定义了两种运算,分别是加法和减法,减法其实就是加法的逆运算,相应的运算都可以在普通的四则运算上针对群元素个数进行求余操作,我们看看上面代码运行的结果:

a = FieldElemet(7, 23)
b = FieldElemet(9, 23)
print(a - b)
print(a * b)

c = FieldElemet(3, 13)
d = FieldElemet(1, 13)
print(c ** 3 == d)

代码运行结果为:
FieldElement with value:21 and order:23
FieldElement with value:17 and order:23
True
我们检验一下, 30 = 21 + 9, 7 = 30 % 23,由此减法实现是正确的,同时 63 = 7 * 9,针对23求余后正好是17,对于指数操作而言,我们同样是在普通的指数运算后再将结果求余,因此3^3的结果是27,然后对13求余所得结果是1,因此它与元素1在求余的基础上是相等的,接下来我们要处理比较烧脑的除法操作,针对有限群的除法需要探究一些数论逻辑,在数论中有关费马小定理为:

n^(p-1) % p = 1

例如针对群F(13),我们随机取一个元素例如5,然后验证一下费马小定理:

print(pow(4, 12, 13))

上面代码运行后结果为1,因此从实践上看定理成立,实际上给定任意整数n,我们把群里面的每个元素都乘以n,然后求余,所得结果还是原来群中的元素,假设我们选定n为5:

#F(13)的群元素
n = 5
field_before = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
field_after = []
for i in field_before:
    field_after.append((i * n) % 13)
print("field_after: ", field_after)

上面代码运行后输出结果为:
field_after: [0, 5, 10, 2, 7, 12, 4, 9, 1, 6, 11, 3, 8]
从结果看,输出的数值都属于群F(13)只不过是元素的位置发生了改变而已,对群而言,元素的位置没有任何意义,只要两个群中元素的数量相同,元素能一一对应,两个群就相同,于是就有:
{1, 2, 3, …, p -1} = {1 *n, 2 *n …, (p-1)*n}
由此我们把左边元素全部相乘然后对p求余就等于右边元素全部相乘后对p求余:
1*2*3…*(p-1) % q= n*(2n)*(3n)…*(p-1)n %q
于是等式左边就是
(p-1)! % q,
右边就是 (p-1)!*(n^(p-1))
左右两步把(p-1)!消掉就有:
(n^(p-1)) % q = 1

为了利用上面的结论,我们可以把除法操作改为乘法操作 a / b 就等价与 a * b^(-1), 在群的定义中,任何一个元素a, 都存在另一个元素a^(-1), 使得两两者进行“*"操作后结果为1,但根据费马小定理,对任何群里面的元素a,我们都有a^(p-1) % q = a * a^(p-2)= 1,于是我们有a(-1)=a(p-2)。于是我们计算除法a/b 时,其实就是计算乘法 a * b^(p-2) % q,于是我们实现群元素除法如下:

def __truediv__(self, other):
        if self.prime != other.prime:
            raise TypeError('there are two different fields')
        num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime
        return self.__class__(num, self.prime)

我们验证一下代码逻辑:

e = FieldElemet(12, 23)
f = FieldElemet(20, 23)
print( e / f)

上面代码输出为:
FieldElement with value:19 and order:23
我们检验一下,20*19 % 23 = 12,由此可见我们实现的除法没有问题。由于我们可以计算a^(-1),那么 a^(-3) = a^(-3) * 1 = a^(-3) * a^(p-1) = a^(p-4),也就是说当我们针对群成员进行指数运算时,指数可以取负数,于是__pow__函数的修改如下:

def __pow__(self, num):
        num = num % self.prime
        num = pow(self.num, num, self.prime)
        return self.__class__(num, self.prime)

有限群是区块链加解密技术的基础,区块链使用了所谓的椭圆曲线进行数据加密,而椭圆曲线的加密功能又得依赖于有限群概念,因此掌握它才能掌握后续的区块链加解密算法,代码在这里下载:https://github.com/wycl16514/finite-field.git


标签:prime,__,self,元素,算法,num,区块,other,底层
From: https://blog.51cto.com/u_16160261/6476558

相关文章

  • 算法面试题:逆时针打印二叉树外围边缘
    给定一颗二叉树如下:要求把二叉树的外边缘按照逆时针的方式打印出来,也就是你需要打印出以下节点:314,6,271,28,0,17,641,257,29,278,7整个二叉树的外边缘分三部分,第一部分是最左边缘,也就是节点314,6,271,28。第二部分是底边节点,他们分别是0,17,641,257,29。第三部分是右边缘,他们分......
  • 面试算法:在整形数组中构建元素之和能整除数组长度的子集
    更详细的讲解和代码调试演示过程,请参看视频如何进入google,算法面试技能全面提升指南假设A是一个整数数组,长度为n,数组中的元素可能是重复的。设计一个算法,找到一系列下标的集合I={i(0),i(1),i(2)….i(n)}.使得(A[i(0)]+A[i(1)]+…A[i(n)])modn=0.例如假定A={711......
  • 面试算法:计算堆栈当前元素的最大值
    更详细的讲解和代码调试演示过程,请参看视频如何进入google,算法面试技能全面提升指南有一道堆栈相关算法题,我被面试过两次以上,看似其在算法面试中出现的概率很高,由此值得我们好好分析下。题目是这样的:对于堆栈的常用操作有,pop弹出堆栈顶部的元素;push向堆栈压入一个元素;peek获......
  • 算法面试:单向链表节点的奇偶排序。
    给定一个单项链表,要求实现一个算法,把链表分成两部分,前一部分全是下标为偶数的节点,后一部分全是下标为奇数的节点,例如给定链表为下图的第一个队列,要求编写一个算法,将链表转换为第二个队列:要求算法不能分配多余的内存,同时,在操作链表是,不能更改节点内部的内容,只能更改节点的next指针......
  • 面试算法:获取重合列表的第一个相交节点
    给定两个单向链表,这两个链表有可能会有重叠,情况如下:两个单向链表从节点5开始重合,要求给定一个空间复杂度为O(1)的算法,返回两个链表相交时的第一个节点。依据上图,也就是返回节点5.首先我们需要做的是,确保给定的两个单向链表,他们是相交的。这个很好确定,只要从头遍历两个链表,如果他们......
  • 算法面试之道:在O(1)的时间内删除单链接链表的指定节点
    对于一个单项链接的链表,给定其中某个任意节点,要求在O(1)的时间复杂度内删除该节点。表面上看起来,似乎不可能做到,因为如果要求时间复杂度是O(1)的话,那意味着,算法实现中,不得包含有任何循环或是对链表的整体遍历。但问题在于,要删除某个指定节点,我们需要通过遍历,找到该节点的前节点,然......
  • 面试算法:波浪型数组的快速排序法
    波浪型数组是具备这样特性的数组,它的元素先是递增,到达某个点后开始低贱,接着达到某个点时又递增,接着右递减,以下数组就是一个波浪型数组:A:57,131,493,294,221,339,418,452,442,190A[0]A[1]A[2]A[3]A[4]A[5]A[6],A[7],A[8],A[9]不难发现,A[0]-A[4]是一个波浪,因为......
  • 字符串哈希算法
    问题描述考虑1044.最长重复子串(Hard),本题思路并不难,可以使用二分答案来解决,假设答案为mid,那么长度大于mid的子串在s中只会出现一次,否则至少出现两次。因此只需要考虑子串在s中的出现次数即可,比较直接的想法是使用key为string的unordered_map,然而unoredere_map......
  • 快速选择算法
    问题描述给定一个长度为$n$的数组,如何在$O(n)$的时间复杂度内找到第$k$大的数。思路朴素的想法是先排序,然后直接找到第$k$个元素,时间复杂度为$O(n\logn)$。我们可以利用快速排序的思想来解决这个问题,考虑快速排序的划分过程,在快速排序的“划分”结束后,数组$A_p\cdotsA_r$被......
  • LRU 算法与 LFU 算法
    算法介绍LRULRU全称是LeastRecentlyUsed,即最近最久未使用算法。LRU根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高,它是页面置换算法的一种,也常用于缓存设计。LFULFU全称是LeastFrequentlyUsed,根据频率来选择要......