【问题标题】:How to calculate first repetition of a looping function?如何计算循环函数的第一次重复?
【发布时间】:2019-05-07 06:07:00
【问题描述】:

我是 Python 新手,对以下作业有疑问。

import random
print(random.randint(1, 100))

重复多少个随机数后如何计算(使用字典)?

输出应该是这样的:

    >>> repeat (1, 100)
    ([random1, random2, random3, ...], number of loops until the first repetition is reached)

谢谢。

【问题讨论】:

    标签: python loops repeat


    【解决方案1】:

    您可以只初始化一个集合,然后在循环中获取一个随机数并检查它是否已经在集合中,如果是则退出,否则将其添加到集合中,如果不是则继续。

    【讨论】:

      【解决方案2】:

      set在这里比dict更适合,因为你只是想记录重复,dict中的值在这里没用,O(1)时间检查set中的重复:

      import random
      def repeat(a, b):
          nums = set()
          count = 0
          while len(nums) == count:
              nums.add(random.randint(a, b))
              count += 1
          return list(nums)
      

      【讨论】:

        【解决方案3】:

        不使用字典,而是使用函数repeat

        import random
        def repeat(a, b):
            l = []
            while True:
                l.append(random.randint(a, b))
                if len(set(l)) != len(l):
                    break
            return l
        
        print(repeat(1, 100))
        

        输出:

        [13, 76, 32, 41, 59, 34, 43, 91, 28, 17, 53, 20, 46, 67, 37, 88, 16, 6, 92, 34]
        

        【讨论】:

          【解决方案4】:

          如果不保持生成数字的顺序,您可以使用字典来执行此操作。

          import random
          def repeat_generator(min, max):
              random_dict = {}
              while True:
                  random_number = random.randint(min, max)
                  if random_dict.get(random_number):
                      break
                  else:
                     random_dict[random_number] = 1
              print(random_dict.keys())
          

          【讨论】:

            【解决方案5】:

            试试下面的代码。这会将每个随机数添加到一个字典中,然后使用它来检查随机数是否已经存在。你可以使用其他数据结构来做同样的事情,但是,因为你提到了字典,所以我在这里使用它。

            import random
            
            rand_dict = {}
            
            i = 0
            while True:
                rand_num = random.randint(1,100)
                if rand_num in rand_dict:
                    print("random number already in dict. rand_num = %s, Number of Loops = %s" %(rand_num, i))
                    break
                else:
                    i += 1
                    rand_dict[rand_num] = 1
            

            输出:

            random number already in dict. rand_num = 90, Number of Loops = 11
            

            【讨论】:

              【解决方案6】:

              您可以保留一个列表,并继续在该列表中添加随机数,当您发现您的随机数已经在您的列表中时,您会打破循环注意,这是您需要的 O(n) 正弦解在整个列表中查找元素

              import random
              
              my_list = []
              
              while True:
                  #Generate random number
                  i = random.randint(1, 100)
                  #If it is already in the list, break the loop
                  if i in my_list:
                      break
                  #Append random number to list
                  my_list.append(i)
              
              print(my_list)
              

              输出可能看起来像

              [5, 58, 84, 53]
              [5, 29, 64, 52, 69, 53, 72, 41, 58, 50, 4, 68, 67, 22, 90, 32, 45, 17, 47, 89, 55, 6, 7, 46, 37, 88]
              [17, 65, 46, 84, 30, 100, 48, 31, 80, 97, 70, 86, 47, 81, 13, 85, 60, 63, 22, 68, 8, 36, 99]
              .....
              

              @6502 已经指出的 O(1) 建议是将随机数附加为字典的键,这样查找会更快O(1)

              import random
              
              my_dict = {}
              
              while True:
                  #Generate random number
                  i = random.randint(1, 100)
                  #If it is already in the keys of dict, break the loop
                  if i in my_dict:
                      break
                  #Append random number as key of dict with value 0
                  my_dict[i] = 0
              
              print(list(my_dict.keys()))
              

              我们实际上可以使用timeit 模块检查改进,尽管它并不明显,因为随机数大小只有 1-100

              首先为列表方法

              In [21]: import random 
                  ...:  
                  ...: def get_list(): 
                  ...:     my_list = [] 
                  ...:     while True: 
                  ...:         #Generate random number 
                  ...:         i = random.randint(1, 100) 
                  ...:         #If it is already in the list, break the loop 
                  ...:         if i in my_list: 
                  ...:             break 
                  ...:         #Append random number to list 
                  ...:         my_list.append(i) 
                  ...:     return my_list 
                  ...:                                                                                                                                                                                                              
              
              In [22]: %timeit get_list()                                                                                                                                                                                           
              16.9 µs ± 720 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
              

              那么对于dict方法

              In [27]:                                                                                                                                                                                                              
              
              In [27]: import random 
                  ...:  
                  ...: def get_list(): 
                  ...:     my_dict = {} 
                  ...:     while True: 
                  ...:         # Generate random number 
                  ...:         i = random.randint(1, 100) 
                  ...:         # If it is already in the keys of dict, break the loop 
                  ...:         if i in my_dict: 
                  ...:             break 
                  ...:         # Append random number as key of dict with value 0 
                  ...:         my_dict[i] = 0 
              In [29]: %timeit get_list()                                                                                                                                                                                           
              16.1 µs ± 567 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
              

              但如果我们使用 1 到 10000 的范围,差异是明显的

              In [38]: import random 
                  ...:  
                  ...: def get_list(upper): 
                  ...:  
                  ...:     my_list = [] 
                  ...:     while True: 
                  ...:         # Generate random number 
                  ...:         i = random.randint(1, upper) 
                  ...:         # If it is already in the list, break the loop 
                  ...:         if i in my_list: 
                  ...:             break 
                  ...:         # Append random number to list 
                  ...:         my_list.append(i) 
                  ...:                                                                                                                                                                                                              
              
              In [39]: %timeit get_list(10000)                                                                                                                                                                                      
              287 µs ± 5.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
              
              In [40]: import random 
                  ...:  
                  ...: def get_list(upper): 
                  ...:    
                  ...:     my_dict = {} 
                  ...:  
                  ...:     while True: 
                  ...:         # Generate random number 
                  ...:         i = random.randint(1, upper) 
                  ...:         # If it is already in the keys of dict, break the loop 
                  ...:         if i in my_dict: 
                  ...:             break 
                  ...:         # Append random number as key of dict with value 0 
                  ...:         my_dict[i] = 0 
                  ...:                                                                                                                                                                                                              
              
              In [41]:  %timeit get_list(10000)                                                                                                                                                                                     
              155 µs ± 2.48 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
              

              您可以看到 list 方法几乎是 dict 方法的两倍!

              【讨论】:

                【解决方案7】:

                这是一个高级解决方案。

                首先,创建一个随机数生成器:

                >>> import random
                >>> random.seed(100) # to make it repeatable
                >>> it = iter(lambda: random.randint(1,100), -1)
                

                这是函数iter 的鲜为人知的形式:第一个参数是一个在到达哨兵之前被调用的函数。由于randint 永远不会返回-1,这是一个无限的随机数生成器:

                >>> import itertools
                >>> list(itertools.islice(it, 15))
                [19, 59, 59, 99, 23, 91, 51, 94, 45, 56, 65, 15, 69, 16, 11]
                >>> list(itertools.islice(it, 15))
                [95, 59, 34, 7, 85, 83, 27, 43, 30, 40, 99, 27, 23, 19, 25]
                

                您可以根据需要获取任意数量的随机数。 (函数islice 采用迭代器的“切片”)。现在,我们在数字未知的情况下取数字:

                >>> seen = set()
                >>> list(itertools.takewhile(lambda x: x not in seen and not seen.add(x), it))
                [45, 48, 81, 53, 27, 52, 60, 72, 36, 49, 21, 84, 82, 16, 24, 1, 78, 51, 19, 100, 73]
                

                注意副作用:我们创建一个空集s 来存储看到的数字,并且对于每个x,我们测试x 是否在seen 中(我们之前看到过x然后x 添加到seen。诀窍是seen.add(x) 总是返回None,因此not seen.add(x) 总是Trueb and not seen.add(x) == b and True == b

                检查:

                >>> random.seed(100) # to make it repeatable
                >>> it2 = iter(lambda: random.randint(1,100), -1)
                >>> list(itertools.islice(it2, 30, 52))
                [45, 48, 81, 53, 27, 52, 60, 72, 36, 49, 21, 84, 82, 16, 24, 1, 78, 51, 19, 100, 73, 21]
                

                21 被重复了。

                【讨论】:

                • -1:这是一个非常错误的“高级”概念。它根本不先进,它只是毫无意义的复杂性。世界上没有理由认为这应该是解决这个问题的合理解决方案,但对于一些奇怪的人类行为问题,这种过于复杂的问题在某种程度上令人着迷。我的想法是这对程序员来说只是一个幼稚的问题,但其他人告诉我,克服它的经验并不是关键因素。无论如何,再一次,最差的就是赢家,这会损害学习者并强化答案作者的误解。
                • @6502 我没想到我的答案会被接受(如果它“不被接受”也不会被激怒),因为它并不是这个问题的主要答案,而是(向 OP 和其他读者)提供有关如何以不同方式解决此问题的见解。这就是为什么我为我的答案写了一个小序言(“高级”这个词可能不是正确的:目的是表明这个答案应该在更明显的答案之后考虑,而不是说它比其他答案更好)。
                • 我想你会同意这个答案只是其他答案(包括你的)背后的想法的另一种实现:1。创建数字; 2.将它们存储在set中; 3. 停在第一个副本上。所以我想这是你觉得不好的实现,因为它过于复杂。 iter 的用法对我来说似乎很简单。 and not seen.add(x) 部分很棘手,但这是一个众所周知的技巧。我想创建一个无限迭代器,看看是否可以在第一个副本上停止,仅此而已。
                • 当然,我不会在生产中使用这段代码,但生产并不是每一段代码的最终目标。当然,这不是你学习 Python 时需要的。但我希望这个答案有存在的权利。
                猜你喜欢
                • 2021-09-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-06-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多