【问题标题】:Return a random word from a word list in python从python中的单词列表中返回一个随机单词
【发布时间】:2009-09-21 20:19:28
【问题描述】:

我想使用 python 从文件中检索一个随机单词,但我不相信我的以下方法是最好的或有效的。请帮忙。

import fileinput
import _random
file = [line for line in fileinput.input("/etc/dictionaries-common/words")]
rand = _random.Random()
print file[int(rand.random() * len(file))],

【问题讨论】:

  • 请注意,尽管在大多数情况下您应该使用 open(),但 file() 仍然是 Python 内置函数(对于 Python 2.x),可能不应该用作变量名。
  • 这些解决方案中的大多数在 Python 3 中都不起作用。

标签: python


【解决方案1】:

random 模块定义了choice(),它做你想做的事:

import random

words = [line.strip() for line in open('/etc/dictionaries-common/words')]
print(random.choice(words))

还请注意,这假定每个单词都单独位于文件中的一行。如果文件非常大,或者如果您经常执行此操作,您可能会发现不断重新读取文件会对应用程序的性能产生负面影响。

【讨论】:

  • 考虑到问题中使用了“高效”一词,加载整个文件似乎没有抓住重点,即使它是最 Pythonic 的可用方法。
  • 整洁...以前从未听说过 random.choice()...现在回去工作 ;)
  • @Oli 如果您对每个单词的字节数没有假设,则必须阅读整个文件才能知道单词的位置。
  • 啊。暂时不要尝试使用 python3。
  • 在 OS X 上字典文件的位置是 /usr/share/dict/words
【解决方案2】:

另一种解决方案是使用getline

import linecache
import random
line_number = random.randint(0, total_num_lines)
linecache.getline('/etc/dictionaries-common/words', line_number)

来自文档:

linecache 模块允许人们获取 任何文件中的任何行,而 试图在内部进行优化, 使用缓存,常见的情况是 从单个文件中读取多行

编辑: 您可以计算一次总数并存储它,因为字典文件不太可能更改。

【讨论】:

  • 我怎么知道这个方法的总行数是多少?
  • 你可以计算一次总数并存储它,因为字典文件不太可能改变。
【解决方案3】:
>>> import random
>>> random.choice(list(open('/etc/dictionaries-common/words')))
'jaundiced\n'

它在人力时间方面是有效的。

顺便说一句,您的实现与 stdlib 的 random.py 中的实现一致:

 def choice(self, seq):
    """Choose a random element from a non-empty sequence."""
    return seq[int(self.random() * len(seq))]  

衡量时间表现

我想知道所提出的解决方案的相对性能如何。 linecache-based 显然是最受欢迎的。与select_random_line() 中实现的诚实算法相比,random.choice 的单行算法慢了多少?

# nadia_known_num_lines   9.6e-06 seconds 1.00
# nadia                   0.056 seconds 5843.51
# jfs                     0.062 seconds 1.10
# dcrosta_no_strip        0.091 seconds 1.48
# dcrosta                 0.13 seconds 1.41
# mark_ransom_no_strip    0.66 seconds 5.10
# mark_ransom_choose_from 0.67 seconds 1.02
# mark_ransom             0.69 seconds 1.04

(每个函数被调用10次(缓存性能)。

这些结果表明,在这种情况下,简单的解决方案 (dcrosta) 比更深思熟虑的解决方案 (mark_ransom) 更快。

用于比较的代码 (as a gist):

import linecache
import random
from timeit import default_timer


WORDS_FILENAME = "/etc/dictionaries-common/words"


def measure(func):
    measure.func_to_measure.append(func)
    return func
measure.func_to_measure = []


@measure
def dcrosta():
    words = [line.strip() for line in open(WORDS_FILENAME)]
    return random.choice(words)


@measure
def dcrosta_no_strip():
    words = [line for line in open(WORDS_FILENAME)]
    return random.choice(words)


def select_random_line(filename):
    selection = None
    count = 0
    for line in file(filename, "r"):
        if random.randint(0, count) == 0:
            selection = line.strip()
            count = count + 1
    return selection


@measure
def mark_ransom():
    return select_random_line(WORDS_FILENAME)


def select_random_line_no_strip(filename):
    selection = None
    count = 0
    for line in file(filename, "r"):
        if random.randint(0, count) == 0:
            selection = line
            count = count + 1
    return selection


@measure
def mark_ransom_no_strip():
    return select_random_line_no_strip(WORDS_FILENAME)


def choose_from(iterable):
    """Choose a random element from a finite `iterable`.

    If `iterable` is a sequence then use `random.choice()` for efficiency.

    Return tuple (random element, total number of elements)
    """
    selection, i = None, None
    for i, item in enumerate(iterable):
        if random.randint(0, i) == 0:
            selection = item

    return selection, (i+1 if i is not None else 0)


@measure
def mark_ransom_choose_from():
    return choose_from(open(WORDS_FILENAME))


@measure
def nadia():
    global total_num_lines
    total_num_lines = sum(1 for _ in open(WORDS_FILENAME))

    line_number = random.randint(0, total_num_lines)
    return linecache.getline(WORDS_FILENAME, line_number)


@measure
def nadia_known_num_lines():
    line_number = random.randint(0, total_num_lines)
    return linecache.getline(WORDS_FILENAME, line_number)


@measure
def jfs():
    return random.choice(list(open(WORDS_FILENAME)))


def timef(func, number=1000, timer=default_timer):
    """Return number of seconds it takes to execute `func()`."""
    start = timer()
    for _ in range(number):
        func()
    return (timer() - start) / number


def main():
    # measure time
    times = dict((f.__name__, timef(f, number=10))
                 for f in measure.func_to_measure)

    # print from fastest to slowest
    maxname_len = max(map(len, times))
    last = None
    for name in sorted(times, key=times.__getitem__):
        print "%s %4.2g seconds %.2f" % (name.ljust(maxname_len), times[name],
                                         last and times[name] / last or 1)
        last = times[name]


if __name__ == "__main__":
    main()

【讨论】:

    【解决方案4】:

    Python 化我来自What’s the best way to return a random line in a text file using C? 的答案:

    import random
    
    def select_random_line(filename):
        selection = None
        count = 0
        for line in file(filename, "r"):
            if random.randint(0, count) == 0:
                selection = line.strip()
            count = count + 1
        return selection
    
    print select_random_line("/etc/dictionaries-common/words")
    

    编辑:我的答案的原始版本使用了readlines,它没有像我想象的那样工作,完全没有必要。此版本将遍历文件而不是将其全部读入内存,并一次性完成,这应该比我迄今为止看到的任何答案都更有效率。

    通用版

    import random
    
    def choose_from(iterable):
        """Choose a random element from a finite `iterable`.
    
        If `iterable` is a sequence then use `random.choice()` for efficiency.
    
        Return tuple (random element, total number of elements)
        """
        selection, i = None, None
        for i, item in enumerate(iterable):
            if random.randint(0, i) == 0:
                selection = item
    
        return selection, (i+1 if i is not None else 0)
    

    示例

    print choose_from(open("/etc/dictionaries-common/words"))
    print choose_from(dict(a=1, b=2))
    print choose_from(i for i in range(10) if i % 3 == 0)
    print choose_from(i for i in range(10) if i % 11 == 0 and i) # empty
    print choose_from([0]) # one element
    chunk, n = choose_from(urllib2.urlopen("http://google.com"))
    print (chunk[:20], n)
    

    输出

    ('是的\n', 98569) ('a2) (6, 4) (无,0) (0, 1) ('window._gjp && _gjp(', 10)

    【讨论】:

    • 通过递增来维护索引是相当不合 Python 的。我可以建议:对于计数,枚举中的行(文件(文件名,“r”)):
    • 我从未使用过枚举,但它看起来是个不错的建议。谢谢。发布到 StackOverflow 的一个意想不到的好处是学习新东西。
    • 我添加了适用于任何有限迭代的通用版本。
    • 马克,如果没有测量,很难判断哪个版本(在所有答案中)更快。
    • 我有根据的猜测是,我对性能的有根据的猜测通常是错误的。我在答案stackoverflow.com/questions/1456617/… 中添加了一些测量值
    【解决方案5】:

    你可以不使用fileinput

    import random
    data = open("/etc/dictionaries-common/words").readlines()
    print random.choice(data)
    

    我还使用了data 而不是file,因为file 是Python 中的预定义类型。

    【讨论】:

      【解决方案6】:

      我没有代码给你,但就算法而言:

      1. 查找文件的大小
      2. 使用 seek() 函数进行随机搜索
      3. 查找下一个(或上一个)空白字符
      4. 返回该空格字符后开始的单词

      【讨论】:

      • 如何在 Python 中找到文件的大小?这肯定会更有效率。
      • os.stat(path).st_size。但是请注意,这种方法并不完全“公平”:长词后面的词更有可能被选中。
      【解决方案7】:

      在这种情况下,效率和冗长不是一回事。很容易选择最漂亮的、pythonic 的方法,它在一两行内完成所有事情,但对于文件 I/O,坚持经典的 fopen 风格的低级交互,即使它确实占用了更多的代码行.

      我可以复制并粘贴一些代码并声称它是我自己的(如果他们愿意,其他人也可以)但是看看这个:http://mail.python.org/pipermail/tutor/2007-July/055635.html

      【讨论】:

      • 在文件中选择一个随机点会使您的选择偏向于更长的词——例如“antidisestablishmentarianism”(或它后面的词,取决于您的实施)出现的可能性是(单词后面)“a”。
      【解决方案8】:

      有几种不同的方法可以优化这个问题。您可以针对速度或空间进行优化。

      如果您想要一个快速但占用大量内存的解决方案,请使用 file.readlines() 读入整个文件,然后使用 random.choice()

      如果你想要一个节省内存的解决方案,首先通过反复调用 somefile.readline() 来检查文件中的行数,直到它返回“”,然后生成一个小于行数的随机数(例如,n ),寻回文件开头,最后调用 somefile.readline() n 次。下一次调用 somefile.readline() 将返回所需的随机行。这种方法不会浪费内存来保存“不必要的”行。当然,如果您打算从文件中获取大量随机行,这将非常低效,最好将整个文件保留在内存中,就像第一种方法一样。

      【讨论】:

      • 您也可以只缓存文件中换行符的位置,这样您就可以使用单个查找命令跳转到特定行。
      猜你喜欢
      • 1970-01-01
      • 2013-12-17
      • 1970-01-01
      • 1970-01-01
      • 2015-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      相关资源
      最近更新 更多