来找独特数啊

本博客第一篇文章的诞生特别鸣谢ly同学的激励与帮助

目录

      • 异或运算
      • 产生测试所用的列表代码
      • 101个数中,有50对数相同,有1个数为奇异数,找出该奇异数
      • 102个数中,有50对数相同,有2个数为奇异数,找出奇异数
      • 103个数中,有50对数相同,有3个数为奇异数,找出奇异数
      • 效果演示

异或运算

  1. 异或运算是位运算,简言之即相同为0,不同为1
  2. 在数学表达中用 ⊕ 符号表示异或,在计算机中用 ^ 符号代表异或运算
  3. 任何数与自己异或为0,a ^ a = 0
  4. 任何数与0异或等于自己,a ^ 0 = a
  5. 异或运算具有交换律,a ^ b ^ a=a ^ a ^ b=b

产生测试所用的列表代码

import random as rd


def product_list(n, odd_num):
    """
    :param n:产生多少对随机数
    :param odd_num: 产生几个奇异数
    :return:
    """
    demo_list = rd.sample(range(100), n)  # randsample会产生n个[0,100)互不相同的随机数
    demo_list.extend(demo_list)  # 我扩展我自己,这样demo_list中就有n对相同的随机数了
    demo_list.append(rd.randint(1000, 2000))
    print('odd1 = %d' % demo_list[n * 2])
    if odd_num == 2:
        demo_list.append(rd.randint(2000, 3000))
        print('odd2 = %d' % demo_list[n * 2 + 1])
    elif odd_num == 3:
        demo_list.append(rd.randint(2000, 3000))
        print('odd2 = %d' % demo_list[n * 2 + 1])
        demo_list.append(rd.randint(3000, 4000))
        print('odd3 = %d' % demo_list[n * 2 + 2])
    return demo_list

101个数中,有50对数相同,有1个数为奇异数,找出该奇异数

  • 由上述异或性质5,3,可将101个数重新排列为相同的数相邻,最后一位为奇异数,将数组从头异或至尾,即可得到该奇异数
def find_1odd(demo_list):
    odd = 0
    for element in demo_list:
        odd ^= element
    return odd

102个数中,有50对数相同,有2个数为奇异数,找出奇异数

  • 这里要引出分堆器的思想,也是代码思想中常用的分而治之的思想,如何产生一个分堆器将两个奇异数分到两堆之中,进而可以直接利用上面的find_1odd?

    • 这里先讲方法,具体思想可参考博客12. 【C语言】 102个整数中有50个数出现了两次,2个数出现了一次, 找出出现了一次的那2个数(2_task)_DennisCheng520的博客-CSDN博客
    • 就是利用find_1odd先得到初始分堆器divider,由于异或性质5,3,这个divider其实就是那两个奇异数异或的结果
    • 之后divider = divider & (-divider)才会得到真正的分堆器,这个分堆器中只有1位为1,剩余位全是0,而两个奇异数的对应的这一位恰巧一位为0,一位为1。
    • 至此,根据divider与列表中元素相与的结果是否为0,就可将原来的列表分为两堆,再对这两堆分别利用find_1odd,即可得到两个奇异数
    def find_2odd(demo_list):
        # divider可翻译为分堆器,这里只是初始分堆器
        divider = find_1odd(demo_list)
        # 这里生成了真正的分堆器,分堆器根据列表中元素某一位为0或1将列表分为两堆,两个奇异数会被分开
        divider = divider & -divider
        list0, list1 = [], []
        for element in demo_list:
            if divider & element == 0:
                list0.append(element)
            # elif:divider&element==1: 这一句的使用是错误的,思考为什么?
            else:
                list1.append(element)
        odd0 = find_1odd(list0)
        odd1 = find_1odd(list1)
        return odd0, odd1
    

103个数中,有50对数相同,有3个数为奇异数,找出奇异数

  • 这里同样要利用分堆的思想,将三个奇异数分为两堆,一堆含有一个奇异数,另一堆含有另外两个奇异数。
  • 因为尚未找到巧妙的方法直接找到分堆器,因此这里使用暴力破解的方法,利用循环左移,依次使用1,10,110,1110,11110…这些分堆器,来对列表进行分堆。
  • 但这有一个小坑就是可能当前使用的分堆器1所在的位对应的三个奇异数的那位相同,此时三个奇异数会被分到一堆,也就无法找出奇异数了。
  • 因此在分堆之后,分别对两堆异或的结果进行监测,若两分堆异或的结果均不为0,即可使用find_1odd和find_2odd正确找到奇异数
def find_3odd(demo_list):
    divider = 1
    result = [0] * 3
    for i in range(32):  # 暴力使用32个分堆器:1,10,100,1000...
        list0 = []
        list1 = []
        for element in demo_list:
            if element & divider == 0:
                list0.append(element)
            else:
                list1.append(element)
        result0 = find_1odd(list0)
        result1 = find_1odd(list1)
        if result0 != 0 and result1 != 0:
            if len(list0) % 2 == 1:
                result[0] = result0
                result[1], result[2] = find_2odd(list1)
                break
            elif len(list1) % 2 == 1:
                result[0]= result1
                result[1],result[2] = find_2odd(list0)
                break
        divider <<= 1  # 分堆未成功,则使用新的分堆器
    return result

效果演示

if __name__ == '__main__':
    demo_list = product_list(50, 3)
    result = find_3odd(demo_list)
    print(result)

在这里插入图片描述

你可能感兴趣的:(python学习,python,算法)