模块化字符比较

5
这似乎是一个非常简单的问题,但互联网上关于此的写作很少,我自己在正确实现它方面遇到了困难。在Java中,实现一种模块化的ASCII字符比较函数的最佳方法是什么,使得比较可以“环绕”字母表的末尾?我想将其用于“between”函数,该函数可以在任意位置将整个字母表分区,并在询问'y'是否在'x'和'b'之间时正确返回“true”。
我已经找到了所有关于字符的模块化算术的问题和答案,因此我知道如何使用类似以下代码的模块化加法(字符移位):
char shifted = (((original - 'a') + 1) % 26) + 'a';

然而,这是基于Java内置的模数运算函数,没有等价的比较方式。即使我使用了普通的整数,我也无法询问Java是否 a < b < c mod 26(如果 a = 24,b = 25 和 c = 1,则应返回true)。
因此,总的问题是,在Java中实现模数比较操作的最佳方法是什么?如果这个问题太难了,那么至少是否有一种方法可以让这样的比较针对ASCII字母表起作用?

请注意,在二进制比较中,“模数比较”是没有意义的:在模算术中不存在“小于”顺序。不过,“介于之间”的概念仍然是有可能的,因此您的问题仍然有效。 - MvG
3个回答

2
在循环队列中测试 A < B < C,您可以始终假设A <= B并且已经绕过或没有绕过。
如果A < B,则未发生绕包。 如果B < C or C < A,那么B在A和C之间。
如果A > B,那么您已经绕过了。 如果B < C and C < A,则B在A和C之间。
您需要自己定义如何处理A == B,B == C或A == C。

这个设计非常巧妙。最好的部分是它不依赖于字母表的长度,因此即使我的字符串可能包含标点符号或Unicode字符,我也可以使用相同的函数而无需更改神奇数字26。 - Edward
它也适用于值可比较但不一定从头到尾连续的字母表,例如十六进制或base64。 - phatfingers

1

所以你的问题是:在字母表环绕的情况下,字符c_1是否在字符c_2c_3之间?

  • 将每个字符转换为数字(例如,a = 1b = 2,...,z = 26)。在您的示例中,这将是c_1 ='y'= 25c_2 ='x'= 24c_3 ='b'= 2之间。
  • 如果c_3 < c_2,则将26添加到c_3。在您的示例中,这是因为2 < 24
  • 现在我们有c_1 = 25c_2 = 24c_3 = 28
  • 检查是否满足c_1 >= c_2 && c_1 <= c_3。如果成立,则该字符位于两个边界之间。如果不成立,则继续下一步。
  • 将26添加到c_1并检查此值是否满足上述检查。如果成立,则该字符位于包装边界内。如果不成立,则停止。
在这种方法中,您基本上是将26添加到“第二个”字母表。因此:
... 23 24 25 26 1 2 3 4

变成:

... 23 24 25 26 27 28 29 30

然后您可以像平常一样进行算术运算。

编辑:根据MvG的评论更新了算法。确实存在多种情况:“25是否在24和2之间?”但也有“1是否在24和2之间?”在后一种情况下,您还需要检查(1 + 26)是否在24和(2 + 26)之间 - 这是成立的,因此字符'a'确实在'x'和'b'之间。


你还应该将26加到c_1上,看看它是否在范围内。 - MvG

0

根据您的喜好,您可能希望使用模数运算符而不是if语句。要在一行中执行比较,请执行以下操作:

public static boolean isStrictlyBetween(char a, char x, char b) {
    // assuming x, a, and b are all the same case (lower or upper).
    return ((x - a + 26) % 26) < ((b - a + 26) % 26);
}

这段代码的意思是,如果从a到x的顺时针距离小于从a到b的顺时针距离,则返回true。+26确保差值表达式的结果为正数(在某些语言中,负数的百分比表现奇怪)。% 26执行所需的模运算。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接