【问题标题】:Generate all possible n-character passwords生成所有可能的 n 字符密码
【发布时间】:2014-03-30 01:10:48
【问题描述】:

作为学习围棋练习的一部分,我正在编写一个简单的暴力破解密码破解程序。

要生成所有可能在 Python 中使用字符 A-E 的 2 字符密码,我会使用 itertools.product()

from itertools import product
for permutation in product('ABCDE', repeat=2):
  print permutation

但是,我在 Go 中很难做到这一点。

Other questions 似乎是关于排列,这不是我想要的相当。虽然 Python 文档包含该函数的示例实现,但我不知道如何将 yield 翻译成 Go。

我想我应该提到两个限制:

  1. 我希望密码的长度是可变的。也就是说,我可能想做 8 个字符的密码,或者 6 个字符的密码,或者别的什么。这意味着我们不能只嵌套 n 个循环。
  2. 我不想一次将所有这些都存储在内存中。

【问题讨论】:

    标签: go


    【解决方案1】:

    你想要的基本上是一个集合的n-ary cartesian product。因此,对于您想要 Prod(set,set,set) 的所有 3 字符密码。这可以迭代构建。首先构造n-1个产品,然后对初始集合的每个产品和每个元素,添加元素。因此,例如所有 2 个字符的密码 -> 3 个字符的密码,其中唯一有效的字符是“a”或“b”。

    "ab" = {a,b} -> {(a,a),(a,b),(b,a),(b,b)} -> {(a,a,a),(a,a,b),(a,b,a),(a,b,b),(b,a,a),(b,a,b),(b,b,a),(b,b,b)}

    func NAryProduct(input string, n int) []string {
        if n <= 0 {
            return nil
        }
    
        // Copy input into initial product set -- a set of
        // one character sets
        prod := make([]string, len(input))
        for i, char := range input {
            prod[i] = string(char)
        }
    
        for i := 1; i < n; i++ {
            // The bigger product should be the size of the input times the size of
            // the n-1 size product
            next := make([]string, 0, len(input)*len(prod))
    
            // Add each char to each word and add it to the new set
            for _, word := range prod {
                for _, char := range input {
                    next = append(next, word + string(char))
                }
            }
    
            prod = next
        }
    
        return prod
    }
    

    游乐场版:http://play.golang.org/p/6LhApeJ1bv

    需要注意的是,这个解决方案还有很大的改进空间。如果要构造长度为 6-18 的所有密码,则为每个密码单独调用此方法将重新计算先前计算的集合。我会留给你写更好的版本。鉴于我向您展示的内容,修改代码以获取任意 (n-m) 元乘积并从中计算 n 元乘积应该不会太难。 (提示:想想你将如何递归地执行此操作)

    【讨论】:

      【解决方案2】:

      例如,满足您的限制,

      package main
      
      import "fmt"
      
      func nextPassword(n int, c string) func() string {
          r := []rune(c)
          p := make([]rune, n)
          x := make([]int, len(p))
          return func() string {
              p := p[:len(x)]
              for i, xi := range x {
                  p[i] = r[xi]
              }
              for i := len(x) - 1; i >= 0; i-- {
                  x[i]++
                  if x[i] < len(r) {
                      break
                  }
                  x[i] = 0
                  if i <= 0 {
                      x = x[0:0]
                      break
                  }
              }
              return string(p)
          }
      }
      
      func main() {
          np := nextPassword(2, "ABCDE")
          for {
              pwd := np()
              if len(pwd) == 0 {
                  break
              }
              fmt.Println(pwd)
          }
      }
      

      输出:

      AA
      AB
      AC
      AD
      AE
      BA
      BB
      BC
      BD
      BE
      CA
      CB
      CC
      CD
      CE
      DA
      DB
      DC
      DD
      DE
      EA
      EB
      EC
      ED
      EE
      

      【讨论】:

      • 啊,我误解了“不是一次全部在内存中”。我认为这意味着他不想一次将所有 1-char AND 2-char AND 3-char 等密码存储在内存中。并不是说他不想将所有 n 字符密码作为列表返回。你完全正确地认为闭包是正确的。
      • 整洁!据我所知,该算法基本上是在基础len(c) 中计算n 数字。
      • @NickCraig-Wood:是的。该算法的一个关键元素是递增一个非负的n-数字,基数len(c) 整数,溢出时会出现NaN 异常。
      【解决方案3】:

      使用通道选择来生成唯一密码

      func randombitsGen(l int) (out chan string) {
      Capschar := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
      smallchar := "abcdefghijklmnopqrstuvwxyz"
      nums := "0123456789"
      specials := "!@#$%ˆ&*()?><"
      out = make(chan string, l)
      defer close(out)
      for {
          select {
          case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
          case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
          case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
          case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
          case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
          case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
          case out <- string(nums[rand.Intn(len(strings.Split(nums, "")))]):
          case out <- string(specials[rand.Intn(len(strings.Split(specials, "")))]):
          default:
              return
      
          }
      }
      

      }

      【讨论】:

      • 这只会输出一个(随机)密码。这不是 OP 所要求的:一种确定性的方式来懒惰地评估所有可能的“密码”。这里的频道使用也有些问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多