具体过程
一、初始化方法:
属性输入阈值的大小InputSize,输出阈值的大小OutSize,输出位的大小OutBit,隐私预算PrivacyParameters,
这个方法中输入的值是:阈值的大小AphbetSize,隐私参数,编码精度3个参数
InputSize=AphbetSize
OutSize=int(math.pow(2,math.ceil(math.log(AphbetSize+1,2))))
(上式中存在math类的三个函数,math.pow(),math.ceil(),math.log()
math.pow(2,3)表示2的3次方,输出结果为8;math.ceil(2.1)表示去2.1向上的最大整数,输出结果为3;math.log(100,10)表示以10为底100的对数,输出结果为2。
上式中先对阈值求2的对数,再作为次方求其值,比如AphbetSize=20,输出的结果OutSize=32,总之输出值的大小比输入的可能值要大。
)
OutBit=int(math.ceil(math.log(AphbetSize+1,2))) 当AphbetSize=7,输出结果OutBit=3,总之输入阈值较大时,输出位仍然会很小。
PrivacyParameters=1/(1+math.exp(PrivacyParameters)) 函数math.exp(3)表示e的3次方,得到的结果是20,PrivacyParameters最后的结果其实是某位(0或1)反转的概率。
在编码方式中,有encode_acc=0和encode_acc=1两种,前者是精确编码,后者是快速编码。
那么接下来要说如何利用哈达玛矩阵快速编码:
def Hadarmard_init(k): H = [None] * k for row in range(k): H[row] = [True] * k print(H)
这里的参数k表示OutSize输出位的大小,其实就是矩阵行数和列数都为k的方阵,矩阵的初始化阶段其实就是将一个输出位转化为了矩阵的一列。
二:奇偶校验
def parity_check(self,x,y): #check if the hadamard entry is one (return 0 for if the entry is 1) z = x&y #bit and z_bit = bin(z)[2:].zfill(self.outbit) check = 0 for i in range(0,self.outbit): #check = sum_i (x_i y_i) (mod 2) check = check^int(z_bit[i]) return check
这个方法的主要作用检查哈达玛矩阵的每一项是否为1,如果为1的项返回0。
具体分析:
z=x&y 先传入两个整型的参数x和y,对其进行求位和&运算,&运算先将x和y转化为二进制,都为真则取真,比如5&3可以计算为0101&0011=0001=1
z_bit=bin(z)[2:].zfill(self.outbit) ,这里的outbit就是OutSize,相对于输入的阈值其实是很小的位数。
outbit=4 z=2 print(bin(z)) #将2转化成二进制,并在之前用ob区分 print(bin(z)[2:]) #取二进制位 print(bin(z)[2:].zfill(outbit)) #将二进制填充至输出位的长度 输出的结果为: 0b10 10 0010
check=check^int(z_bit[i]) ^异或运算符,当对位不同输出1,相同输出0,比如输入值=101^110,结果=011
三、编码单个符号
bitin = bin(int(in_symbol)+1)[2:].zfill(self.outbit) out1 = random.randint(0,math.pow(2,self.outbit)-1) bitout1 = bin(out1)[2:].zfill(self.outbit)
bitin=bin(int(in_symbol)+1)[2:].zfill(self.outbit)
in_symbol是输入的位,将其转化为和输出位相等的二进制字符串
out1=random.randint(0,math.pow(2,self.outbit)-1)
在可能的输出大小中随机选择一位OutSize
bitout1=bin(out1)[2:].zfill(self.outbit)
将随机取中的这一位转换为和输出位相等的二进制字符串
for i in range(0,self.outbit): if int(bitin[i]) == 1: out2 = out1 ^ (pow(2,self.outbit - i -1)) break
对Out1再进行扰动,对于输入二进制串的最左边的1进行扰动 out2=out1^(pow(2,self.outbit-i-1))
这一步骤相当于在可能输出值中随机抽取一位,对这一位再进行确定性的扰动,得到可能潜在的输出。
if self.ifencode_acc == 1: check = 1 - self.H[int(in_symbol)+1][out1] else: check = 0 for i in range(0,self.outbit): # check if the Hadamard entry at position (in_symbol+1, out1) is one or not check = check^(int(bitout1[i])&int(bitin[i]))
ra = random.random() if check == 0:if ra > self.pri_para: return out1 else: return out2 else: if ra > self.pri_para: return out2 else: return out1
ra=random.random()从0-1中随机选择一个小数
将其和隐私参数比较,如果ra大于隐私参数,则不进行扰动输出out1,否则进行扰动输出out2
四、编码串(多个符号)
def encode_string(self,in_list): out_list = [self.encode_symbol(x) for x in in_list] return out_list
其实就是将输入的列表,将其中的每个元素进行编码,再将其编码后的列表输出。
对于编码部分个人的一些理解:
首先用户的某个属性的阈值是确定的,比如成年人的学历作为一个属性,假设输入值有高中、大专、本科、硕士,我们将用[k]={0,1,2,3}表示,然后将其映射到哈达玛矩阵
映射方式是将每个符号用二进制表示,二进制长度和输出位大小相同,如果位数不够左边用0补齐。比如0映射为矩阵中第一列,1映射为第二列,以此类推...
那么编码之后应该如何对其进行扰动:
当然有一点应该明确,输入的阈值(上面的学历属性)确定后,输出位的大小和可能输出值的大小就确定了,我们在可能的输出值中随机选择一位,并将其转换为二进制串
并且对其最左边为1的位进行反转得到潜在的输出,再随机取一个0-1的概率值和之前的隐私参数比较,来选择返回反转前的值还是反转后的值,这就其大致的扰动方式。
其实在上面的encode_string函数中,in_list并不是一个列表,而是一种自定义数据类型,类似于[14 6 4 ... 1 5 20]方式。
现在需要补充一点重要的知识点:
输入字符串的类型,ndarray类型,这种类型其实就是一个列表,但是其必须要求列表中的元素类型相同,比如[1 3 6 8 9 5],或者['red' 'blue' 'purple' 'pink' 'black']
ndarray和list的转化方式为:
五、解码串
l = len(out_list) count,edges = np.histogram(out_list,range(self.outsz+1)) dist = count/float(l)
l=len(out_list) out_list是编码后输出的串,获得其长度;
count,edges=np.histogram(out_list,range(self.outsz+1))
np.histogram()中的前两个参数,第一个参数out_list类似于数组,第二个参数range(self.outsz+1)如果outsz去8表示0-9,
对于返回值edges表示横坐标的边数,count表示每个范围内的数量,比如0-1这一个段有的数量;
dist=count/float(l) 求得是一个百分比,每一个区域占的百分比;
if iffast == 1: # if we use fast hadamard transform dist_mid = FWHT_A(self.outsz, dist) # do fast hadamard transform to the frequency vector dist_S = (dist_mid[1:self.insz + 1] + 1) / float(2) # get the frequencies of C_i
如果使用快速编码:
dist_mid=FWHT_A(outsz,dist),
def FWHT_A(k, dist): if k == 1: return dist dist1 = dist[0 : k//2] dist2 = dist[k//2 : k] trans1 = FWHT_A(k//2, dist1) trans2 = FWHT_A(k//2, dist2) trans = np.concatenate((trans1+ trans2, trans1 - trans2)) return trans
下面举一个实际例子现实编码的解码的过程:
1、首先生成编码字符串:
prob = prob_list['Uniform']
in_list = np.random.choice(10, 10, p=prob)
-RR的期望差()
k-RAPPOR的期望差()
k-HR的期望差()