【问题标题】:Mnemonic Password Generation Algorithm for QWERTY KeyboardsQWERTY 键盘的助记密码生成算法
【发布时间】:2011-01-13 14:58:56
【问题描述】:

我有一个 "mnemonic" 密码生成函数,它是这样的:

function Mnemonic($mnemonic)
{
    $result = null;
    $charset = array(str_split('aeiou', 1), str_split('bcdfghjklmnpqrstvwxyz', 1));

    for ($i = 1; $i <= $mnemonic; $i++)
    {
        $result .= $charset[$i % 2][array_rand($charset[$i % 2])];
    }

    return $result;
}

基本上,这会生成一个长度为$mnemonic 的字符串,其中每个奇数字符都是辅音,每个偶数字符都是元音。虽然我理解这会降低密码的复杂性,但它通常更容易记住。现在我想通过生成易于输入的字符串来改进它。

例如,虽然我是一个 *nix 新手,但我总是更喜欢基于 RHEL 的发行版而不是 Debian 发行版,主要原因是易于键入 yum 而不是易于键入 apt[-get],你自己试试吧。

我应该如何实现生成易于在 QWERTY 键盘上键入的字符串的逻辑?

【问题讨论】:

  • 定义“更容易输入”。我通常发现我最容易输入的单词就是我能发音的单词 - 所以你的方法非常适合。
  • @Alix Axel,我实际上发现aptyum 更容易打字,特别是因为你可以在打字时换手。
  • 除了“begreen”和“yii”之外,由于双字母的原因,我输入的速度有点慢。我输入“apt”的速度也比“yum”略快,因为双手交替,而且有时只用两个手指输入“yum”
  • 我必须补充一点,apt 对我来说也比 yum 更容易。希望有足够多的支持者,我们可以说服易于打字对于个人的打字风格是模棱两可的。有些人用一根手指戳键盘;有些人使用课堂上教的严格的打字技巧;有些人使用他们自己种植的方法。我不相信你能找到任何算法为所有人生成易于输入的密码。
  • 这是一个非常有趣的问题...

标签: php algorithm string keyboard qwerty


【解决方案1】:

Carpalx对计算typing effort有很多研究,其中包含:

  • 手指移动距离
  • 手、手指和行处罚
  • 笔画路径

他们的研究成果是Colemak 键盘布局,即claims to be better than Dvorak

但是,它是从您想要的内容向后编写的 - 他们的目标是根据输入找到更好的键盘布局,但您正在尝试根据键盘布局找到简单的输入。

所以 - 即使你可能无法直接使用它,我想你可能会觉得它很有趣(谁知道呢,如果你的 Perl-fu 很强大,你可能能够提取和反转算法,因为它是GPL'd)。

【讨论】:

  • 我的 Perl'fu 不是很强大,但我真的很感谢这个链接,谢谢,
  • 这个答案是基于一些研究的唯一相关答案。其余的都只是猜测。如果没有客观的衡量标准,“更难输入”就毫无意义,而且非常主观。
  • @Vinko。你在说什么?这可能是最不相关的答案。这个答案回答了一个完全不同的问题。那么,如果他们进行“客观研究”并根据一些语料库确定最佳键盘布局呢?这如何帮助提问者弄清楚如何生成密码以便更好地在 QWERTY 键盘上打字?
  • @Moron:我说的是每个人都在没有任何研究的情况下发明启发式算法,这与原始算法一样好(甚至更差),我们永远无法知道,因为它只是猜测,没有关于条款的背景甚至共识。这个答案提供了一个构建实际解决方案的框架,请参阅mkweb.bcgsc.ca/carpalx/?model_parametersmkweb.bcgsc.ca/carpalx/?typing_effort。该模型用于派生新布局并不意味着找不到对固定布局压力较小的字母组合。
  • @Vinko:如果您查看您描述的链接,他们没有他们选择的启发式的支持数据,事实上他们自己说一些参数是主观的。确定最佳启发式是主观的(针对手头的问题(没有双关语))。你所做的任何研究都不会改变这一事实。事实上,你做的研究越多,它可能与 OP 越无关。也就是说,我同意打字努力页面应该是有用的,因此并非无关紧要,但我不认为这比启发式模型的任何其他建议更相关。
【解决方案2】:

您可以消除所有用无名指和小指输入的字符 (q,w,x,z,p),然后吐出用左手和右手输入的字符并在这些字母之间交替。

【讨论】:

  • +1,这是一个很好且易于实施的建议,谢谢!
  • 我尝试实现这一点,但我意识到通过在左右手指键之间切换我的助记函数有点失去意义,因为我只会有“ae”或“(y)uio”作为元音。
  • 随机,每个键
【解决方案3】:

你可能想看看principles used in the Dvorak keyboard

应用于密码生成算法的原则是:

  • 字母应换手输入。
  • 使用易于键入的组合。查看 Dvorak 布局,了解常见的二合字母及其字母的位置。
  • 仅使用最下面一行中的一个字母,或者不使用。让它随机!
  • 您可以将比例设为 2 比 1(右手输入的 2 个字母与左手输入的 1 个字母)。
  • 由于比率是 2 比 1,您将有 2 个连续的字母由同一只手输入,因此您必须确保它们是从键盘外侧向内侧输入的。此原则适用于有向图。

我知道你说它是一个 QWERTY 键盘,但是在 QWERTY 键盘上使用这些原则可以给你一些非常好的结果,比如:

ktrd,ogkdo(德沃夏克语中的“打字机”)

kjg;g;akd;k(仅使用主行)

pjedoildupsk(只是遵循原则的随机密码)

所有讨厌德沃夏克的人,都闭嘴!

我希望这会有所帮助。

【讨论】:

  • 你的建议和@superUntitled提供的建议类似,换手的问题是我的偶数字母中只能使用“ae”或“uio”(见我原来的功能)。你会如何解决这个问题?
  • @Alix Axel:嗯,我的方法比@superUntitled 的方法更灵活,因为我建议您使用 2 比 1 的比率而不是 1 比 1,而且不一定非要如此完全是 RRLRRLRRL,看看随机密码样本,它遵循我的原则(但不是元音)。它是 RRLLRRLRRLR,它可以使用 5 个元音中的任何一个。
  • pjedoildupsk 没有遵循您的指导方针。 up 由右手从内向外输入,oi 也是如此。
【解决方案4】:

也许您可以使用一些启发式方法来衡量“打字的难易程度”。

例如,考虑在转到下一个字符时移动手指的成本。这可能是手指需要移动多远、方向等的函数。

当需要换手指或换手时,您还可以增加额外费用。

在稍微考虑一下成本之后,您可能会找到一个令人满意的解决方案。

希望对您有所帮助。

【讨论】:

  • 这是我的想法,但我无法弄清楚如何实施这种启发式方法。
  • 是计算权重的问题,还是您实际上在编写代码来计算移动成本等方面有问题?
  • @Moron:弄清楚从 char 移动到 char + 1 的成本是我主要关心的......
  • 一种简单的方法是为每对字母计算成本。例如 (j,k) = 1, (j,l) = 2, (m,y) = 3 等将其存储在一个表中,然后查找。为简单起见,您可以在网格上布置字符并计算曼哈顿距离。
  • 对于每 26 个字母,手动计算一对成本将非常繁琐。可以自动加快查找表的生成速度吗?
【解决方案5】:

很好的问题 - 接受上述建议,这里有一个从键 i 到键 j 的距离公式:

权重 = 距离 * a + switch * b + 相同 * c + shift * d + 怪异 * e + start * f

距离是一个值,其他都是0/1值。

距离 - 通过在 QWERTY 键盘上叠加精细网格获得,查找 x,y 并计算距离。距离具有正权重。如果字母组合是使用不同的手(例如 aj、sk、wu...),则距离为零。

Switch - 负权重;切换不错

相同 - aq、qa、az、za 使用相同的手指。同样是积极的

转变 - 任何有转变的事物都是积极的,也是非常糟糕的

奇怪 - 我不知道 $ 或 ~ 是坏的,因为你必须看键盘。

开始 - asdfjkl 开始或结束。可能是消极的和好的,因为你的手指在那里休息。

系数 - 只要相对值看起来合理,就让它们开始。 如果你真的想变得花哨 - 让某人输入几十组数字,使用秒表并拟合回归模型。

实现 - 假设我们有一个六位字符的密码。

现在我需要以每个字母开头的六个字符的最小值。想象一列中有 N 个键的数组。现在想象六列。您最短的密码是通过六列的最短路径(允许循环)。您可能需要添加一些逻辑来消除循环,但这应该是一个很好的第一步。 (我在这里变得懒惰了——可能有一个图论公式可以解决这个问题。)

我敢打赌,以前有人这样做过——尤其是击键部分。

【讨论】:

    【解决方案6】:

    我把下面的一起砸了。这是一个 hack 工作,但它似乎工作得很好。

    <?
    function Mnemonic($mnemonic)
    {
        $result = null;
        $charset = array(str_split('@a3e!1i0ou', 1), str_split('#$*bcdfghjklmnpqrstvwxyz', 1));
    
        $lastchar = ' ';
        for ($i = 1; $i <= $mnemonic; $i++)
        {
          do {
            $char = $charset[$i % 2][array_rand($charset[$i % 2])];
          } while (!nextkey($lastchar, $char));
          $result .= $char;
        }
    
        return $result;
    }
    
    function nextkey($lastchar, $requestchar)
    {
      $map = array();
      $map[] = '!qaz'; // ll
      $map[] = @#wsx1'; // lr
      $map[] = 'ed23'; // lm
      $map[] = '$%^rtfgcvb456'; // li
      $map[] = '&yhnujm7'; // ri
      $map[] = '*()ik89'; // rm
      $map[] = 'olp,.'; // rr
      $map[] = ';[]'; // rl
      $map[] = '!@#$%^&*()[]'; // special chars, don't follow
      $map[] = 'pbvcnmq'; // consonant clusters, don't follwo
    
      if($lastchar == $requestchar) return true;
      foreach($map as $string)
        if(strpos($string, $requestchar) && strpos($string, $lastchar)) return false;
      return true;
    }
    
    printf("%s\n", Mnemonic(8));
    ?>

    【讨论】:

      【解决方案7】:

      构建一个表示键盘的数据结构,并对用于键入每个字符的行、列、手和手指进行编码。编写一个函数,当出现一个字符时,它会根据您开发的灵活规则提供“易于键入的下一个”字符的列表。它可以依赖另一个计算键之间距离的函数。

      就我个人而言,我并不认为用同一只手输入两次字母会很慢:只有在前一个字母使用的手指太靠近时才会很困难。例如,XQ 很难打字,因为我的手必须向上移动才能处理打字所需的相邻手指。但我一点也不觉得 BQ 很难打字,因为当我的食指还在敲 B 的时候,我的小指可以指向 Q。

      AW 也比 QS 更容易打,因为无名指更长,所以自然适合 W,而小指在 A,处于接近静止的位置,而 QS 需要伸展小指和无名指同时出现相互冲突的肌肉紧缩。

      如果您开始建立每个字母与其他字母的映射,您很快就会找到一种合理的方式来表示难易度的各个方面。概括我的 XQ/BQ 示例,您可以使一排更改需要 2 个或更多手指的距离,2 行更改需要 3 个手指的距离,而 3 行更改(可能是数字)需要交替的手。

      我还注意到 WD 和 IL 之间的距离比 SE 和 KO 稍长也改变了难度,因为键的位置略微呈锯齿状。

      通过一些分析(我建议使用 Excel 来“绘制”输入难度)我相信您可以提出一种算法来帮助您构建易于输入的单词。

      如果可能,请尝试输入至少一个数字,并考虑使用空格。

      【讨论】:

        【解决方案8】:

        如果您执行此操作,请在确定从一个字符移动到另一个字符的“成本”时考虑用户的语言环境。如果用户使用不同的键盘布局,易于键入的密码可能会变得相当麻烦。在一种语言的键盘上可能很容易访问的某些键在另一种语言的键盘上可能不可用,而无需额外的修饰键(shift、meta 等)。

        为了保持这个想法的普遍性,我建议忽略哪个字符属于哪个键,而是将键视为具有行和列的数组。每行通常与前一行偏移大约 1/3 的键宽度。考虑到这一点,计算任意两个任意键之间的距离应该不难:

        # Key at top left corner is {0, 0}
        key1 @ {x1, y1}
        key2 @ {x2, y2}
        
        xdistance = absolute_value(x2 - x1)
        ydistance = absolute_value(y2 - y1)
        
        if y1 > y2
          xdistance += (1/3 * ydistance)
        else
          xdistance -= (1/3 * ydistance)
        
        total_distance = square_root(xdistance^2 + ydistance^2)
        

        生成一系列满足您的长度和“易于键入”要求的键位置,然后使用用户的当前键映射将这些索引重新映射为字符。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-08-03
          • 1970-01-01
          • 2010-10-29
          • 2019-03-30
          • 2015-08-11
          • 2023-04-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多