k-匿名隐私保护实验

实验目的

  1. 理解基本的k-匿名算法;
  2. 通过相关程序实现对数据表元组的k-匿名处理;

实验环境

  • 编程语言:Java

  • 硬件设备:IDEA,JDK版本1.8

实验原理

k-匿名的核心思想

K-匿名的核心思想是设法切断数据表中准标识符与敏感属性之间的一对一关系来保护隐私属性。使得在一个数据表中,一条记录的准标识符至少有k-1条记录的准标识符与之相同。即如果使用准标识符查询的话,我们得到的查询结果应至少包含k条记录,并且这k条记录的准标识符完全相同,攻击者进而无法通过准标识符推理出隐私数据。

基本的k-匿名算法

  • 输入:原始数据表Tp, 匿名参数k;
  • 输出:满足k-匿名的数据表Tk
  • 步骤:
    1. 从Tp中依次读取k条记录;
    2. 针对原始记录的属性信息对标识符和不希望发布的信息做抑制处理;
    3. 对准标识符中的属性进行泛化,直到k条记录的准标识符完全相同,将处理后的这k条记录存入到Tk中;
    4. 重复1-3步骤,直到数据全部被处理,若剩余的记录不足k条做抑制处理;

实验要求

  1. 给定原始数据集,根据基本的k-匿名算法编写程序对其实现简单的k-匿名处理,使得匿名化处理后的表通过准标识符查询出来的记录至少有k条;
  2. 给出实验报告说明和源代码;

实验内容

  1. 没有按照实验教程给出的步骤去做,做了一点创新,重新生成了一个新的数据文件及集org,共有1000条数据,由于数据量比较大,这里不做展示,已经放在了附件中;

  2. 编写k-匿名处理程序对原始数据集进行k-匿名处理,假设k=2,在原始数据集中按顺序依次选择2条数据记录后,首先需要去除这2条数据记录中的标识符属性No,使之不会出现在k-匿名后的数据记录中,接着需要对这2条数据记录中的准标识符属性idNumbersexageheight进行泛化处理。

  3. 这里做的和实验指导上的不相同,实验指导上没有将idNumber列为准标识符,本实验也没有将它作为准标识符属性,身份证号是每个人的唯一标志,不能作为准标识符。但是为了提高数据的匿名性,本实验将idNumber的第一个字符作为准标识符,即产生了四个准标识符,具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /* Operation.java */
    //只读取org文件中idNumber的第一位到setIdNumber中
    for (int i = 0; i < org_list.size(); i++) {
    Org org=new Org();
    String org_ori=org_list.get(i);
    String elements[]=org_ori.split(",");
    org.setNo(Integer.parseInt(elements[0]));
    org.setIdNumber(elements[1].charAt(0));
    org.setSex(elements[2].charAt(0));
    org.setAge(Integer.parseInt(elements[3]));
    org.setHeight(Integer.parseInt(elements[4]));
    org.setDisease(elements[5]);
    orgs.add(org);
    }

    //对k条记录中的准标识符进行泛化处理,包括idNumber的第一位idNumberFirst
    K temp=generalize(org_k);
    for (int m = 0; m < k; m++) {
    K out=new K();
    out.setIdNumberFirst(temp.getIdNumberFirst());
    out.setSex(temp.getSex());
    out.setAge(temp.getAge());
    out.setHeight(temp.getHeight());
    out.setDisease(org_k.get(m).getDisease());
    results.add(out);
    }

    //选取k条记录中的第一条记录的属性值作为上述标志属性的默认值,包括idNumberFirst
    if(org_k.indexOf(i)==0) {
    idNumberFirst_flag=i.getIdNumberFirst();
    sex_flag=i.getSex();
    age_min=i.getAge();
    age_max=i.getAge();
    height_min=i.getHeight();
    height_max=i.getHeight();
    }

    //判断idNumberFirst属性的取值是否一致
    String idNumberFirst="";
    if(idNumberFirst_flag!=i.getIdNumberFirst())
    idNumberFirst_flag='*';

    //如果k条记录的idNumberFirst属性的取值有不一致的,就均设为*,否则不变
    if(idNumberFirst_flag == '*')
    idNumberFirst="*";
    else
    idNumberFirst=""+idNumberFirst_flag;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* Org.java */
    //在org.java中添加idNumberFirst,并添加函数获取和发送idNumberFirst
    private char idNumberFirst;
    public char getIdNumberFirst() {
    return idNumberFirst;
    }
    public void setIdNumber(char idNumberFirst) {
    this.idNumberFirst = idNumberFirst;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* k.java */
    //在k.java中添加idNumberFirst,并添加函数获取和发送idNumberFirst
    private String idNumberFirst;
    public String getIdNumberFirst() {
    return idNumberFirst;
    }
    public void setIdNumberFirst(String idNumberFirst) {
    this.idNumberFirst = idNumberFirst;
    }

    经过k-匿名处理后,输出了1000条数据记录,但每条记录中只剩下泛化后的准标识符属性值和敏感属性值,理论上通过任意一组准标识符属性查询到的数据记录均有两条,用来切断准标识符属性和敏感属性的一对一关系,满足了k-匿名的处理要求,这里开始按照实验指导书上的要求做完实验后满足上述要求,但是由于自己做了创新后,输出1000条数据,没法准确判断,这里做数据截图如下,生成的完整数据会放到附件里,这里加入一个准标识符后比之前匿名化提供的信息更多;

实验步骤

  1. 运行DataGerenerate.java生成1000条数据集合org;

  2. 设置匿名参数k=2

    1
    int k=2;
  3. 创建operation对象,用于调用其内部的方法

    1
    Operation operation=new Operation();
  4. 获取原始数据集记录,并用ArrayList进行存储;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    ArrayList<Org>  org_lists=operation.getOrgData();
    //读取原始的数据集
    public ArrayList<Org> getOrgData() throws Exception {
    ArrayList<String> org_list=new ArrayList<String>();
    File file=new File("src/k_anonymity/org");
    if (!file.exists()) {
    System.out.println("文件不存在!");
    }
    FileReader fReader=new FileReader(file);
    BufferedReader bufferedReader=new BufferedReader(fReader);
    String str;
    // 按行读取字符串
    while ((str = bufferedReader.readLine()) != null) {
    org_list.add(str);
    }
    bufferedReader.close();
    fReader.close();
    ArrayList<Org> orgs=new ArrayList<Org>();
    for (int i = 0; i < org_list.size(); i++) {
    Org org=new Org();
    String org_ori=org_list.get(i);
    String elements[]=org_ori.split(",");
    org.setNo(Integer.parseInt(elements[0]));
    org.setIdNumber(elements[1].charAt(0));
    org.setSex(elements[2].charAt(0));
    org.setAge(Integer.parseInt(elements[3]));
    org.setHeight(Integer.parseInt(elements[4]));
    org.setDisease(elements[5]);
    orgs.add(org);
    }
    return orgs;
    }
  5. 对原始数据进行k-匿名化处理;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ArrayList<K> results=operation.k_anonymous(org_lists, k);
    //k-匿名处理
    public ArrayList<K> k_anonymous(ArrayList<Org> org_lists, int k) {
    // TODO Auto-generated method stub
    //用于存放匿名化后的数据
    ArrayList<K> results=new ArrayList<K>();
    //存放临时选取的k个原始记录
    ArrayList<Org> org_k=new ArrayList<Org>();
    int i=0;
    for (Org j : org_lists) {
    org_k.add(j);
    i++;
    if(i%k==0) {
    //对k条记录中的准标识符进行泛化处理
    K temp=generalize(org_k);
    for (int m = 0; m < k; m++) {
    K out=new K();
    out.setIdNumberFirst(temp.getIdNumberFirst());
    out.setSex(temp.getSex());
    out.setAge(temp.getAge());
    out.setHeight(temp.getHeight());
    out.setDisease(org_k.get(m).getDisease());
    results.add(out);
    }
    //清除已经处理过的k个记录,用于存储下次选取的k条记录
    org_k.clear();
    }
    }
    return results;
    }
  6. 对原始数据中的准标识符进行泛化;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    //对原始数据中的准标识符进行泛化
    private K generalize(ArrayList<Org> org_k) {
    K temp=new K();
    char idNumberFirst_flag=' ';
    char sex_flag=' ';
    int age_min=0;
    int age_max=0;
    int height_min=0;
    int height_max=0;
    for (Org i : org_k) {
    //选取k条记录中的第一条记录的属性值作为上述标志属性的默认值
    if(org_k.indexOf(i)==0) {
    idNumberFirst_flag=i.getIdNumberFirst();
    sex_flag=i.getSex();
    age_min=i.getAge();
    age_max=i.getAge();
    height_min=i.getHeight();
    height_max=i.getHeight();
    }
    //判断idNumberFirst属性的取值是否一致
    if(idNumberFirst_flag!=i.getIdNumberFirst())
    idNumberFirst_flag='*';
    //判断sex属性的取值是否一致
    if(sex_flag!=i.getSex())
    sex_flag='*';
    //判断Age的取值范围
    if(age_min>i.getAge())
    age_min=i.getAge();
    if(age_max<i.getAge())
    age_max=i.getAge();
    //判断height的取值范围
    if(height_min>i.getHeight())
    height_min=i.getHeight();
    if(height_max<i.getHeight())
    height_max=i.getHeight();
    }

    String idNumberFirst="";
    String sex="";
    String age="";
    String height="";

    //如果k条记录的idNumberFirst属性的取值有不一致的,就均设为*,否则不变
    if(idNumberFirst_flag == '*')
    idNumberFirst="*";
    else
    idNumberFirst=""+idNumberFirst_flag;

    //如果k条记录的sex属性的取值有不一致的,sex就均设为*,否则不变
    if(sex_flag == '*')
    sex="*";
    else
    sex=""+sex_flag;

    //如果k条记录中年龄的最大值和最小值相等
    if(age_min%10==5 && age_max%10==5 && age_min==age_max )
    {
    age_min=(age_min/10)*10;
    age_max=age_min+5;
    }
    age=age_min+"~"+age_max;

    //如果k条记录中身高的最大值和最小值相等
    if(height_min%10==5 &&
    height_max%10==5 &&
    height_min==height_max)
    {
    height_min=(age_min/10)*10;
    height_max=age_min+5;
    }
    height=height_min+"~"+height_max;

    temp.setIdNumberFirst(idNumberFirst);
    temp.setSex(sex);
    temp.setAge(age);
    temp.setHeight(height);
    return temp;
    }
  7. 将匿名化后的数据输出到k中,程序结束;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    operation.printResults(results);
    System.out.println("done");
    //输出匿名化后的结果
    public void printResults(ArrayList<K> results) throws Exception {
    File file=new File("src/k_anonymity/k");
    if (!file.exists()) {
    file.createNewFile();
    }
    FileWriter writer=new FileWriter(file);
    BufferedWriter bufferedWriter=new BufferedWriter(writer);
    for (K k : results) {
    bufferedWriter.write(k.toString());
    bufferedWriter.newLine();
    bufferedWriter.flush();
    }
    bufferedWriter.close();
    }