将一个数字转换为特定范围内的等效数字

3
我有困难需要解决,我想将一个数字转换为给定范围内的“相等”数字。
虽然这个概念非常简单,但很难解释,所以我会尽力而为。

假设您有一排无限数量的瓷砖,但尽管有无限数量的瓷砖,但重复的确切颜色图案是相同的,就像这样: A row of coloured tiles, with the same color pattern repeating again and again. Each tile has a number.

正如您所看到的,我已经给每个瓷砖编号,但我们可以取一个范围,从0到4,然后说: 0是紫色
1是蓝色
2是红色
3是绿色
4是黄色

但是由于这种颜色模式无限重复,实际上,5将与0具有相同的颜色,6将与1具有相同的颜色,8将与3具有相同的颜色。
此外,我们也不能忘记负数,例如-1将与4具有相同的颜色。

实际上,我要做的是将任何给定的数字转换为我选择的范围内的数字,这里是范围[0;4],以便我将知道哪种颜色对应于这个特定的瓷砖。
我还希望此方法适用于其他范围,例如,如果我的范围是[1;5],甚至是[-7;-3]也应该适用。

我已经找到了一种适用于[0;n]范围(其中n为正整数)且a为另一个正整数的方法:

convertedNumber = a % (n+1)

这是在playground中的输出结果: 我们正在进行转换,它的表现符合预期
但是它只在我描述的条件下起作用,在与之斗争了数小时后,我仍然找不到任何好的解决方案,使其适用于任何数字,无论是正数还是负数,以及任何范围。
如需更多细节,请在评论中询问。
谢谢。

什么是范围?如果给定的范围是[1,5]...这里的1仍然代表蓝色(因为它是第一种颜色)还是红色(因为它是1的原始数字)? - Karthik
也许将最大值和最小值的差添加到数字中,最大值和最小值就能起作用了? - RhinoDevel
@karthik 当我说一个范围,比如[a,b],我指的是包含在a和b之间的所有整数,包括a和b。所以对于[1,5],我们将有1、2、3、4和5。 - Pop Flamingo
https://en.wikipedia.org/wiki/Modulo_operation - Hot Licks
那么对于 -9,您想返回 1 吗? - IVlad
@TrevörAnneDenise 如果你的范围是[-7,-3],而你的数字是-9,你想将它映射到-6,对吗? - Karthik
3个回答

3
func modFromRange(range: Range<Int>, ind: Int) -> Int {
  let endIndex = abs(range.endIndex.predecessor() - range.startIndex).successor()
  let i = (ind - range.startIndex) % endIndex
  return i < 0 ? (range.startIndex + i + endIndex) : (range.startIndex + i)
}

modFromRange(1...5, ind: 1)      //  1
modFromRange(-7...(-3), ind: -8) // -3

这类似于:

modFromRange(-7...(-3), ind: -7) // -7
modFromRange(-7...(-3), ind: -2) // -7
modFromRange(-7...(-3), ind: -8) // -3
modFromRange(-7...(-3), ind:  1) // -4

modFromRange(1...5, ind:  1) // 1
modFromRange(1...5, ind:  5) // 5
modFromRange(1...5, ind:  6) // 1
modFromRange(1...5, ind:  0) // 5
modFromRange(1...5, ind: -1) // 4
modFromRange(1...5, ind: -9) // 1

For -7...-3:

[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1,  0,  1,  2,  3,  4,  5,  6,  7]
[ -5, -4, -3, -7, -6, -5, -4, -3, -7, -6, -5, -4, -3, -7, -6, -5, -4, -3]

For 1...5:

[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[ 5,  1,  2,  3,  4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4,  5]

或者,在Swift 2上,您可以将其作为方法执行:

extension Range where T : IntegerLiteralConvertible, T : IntegerArithmeticType {
  func mod(ind: T) -> T {
    let _endIndex = {$0 >= 0 ? $0 : $0 * -1}(endIndex - startIndex)
    let i = (ind - startIndex) % _endIndex
    return i < 0 ? (startIndex + i + _endIndex) : (startIndex + i)
  }
}

这将像下面这样工作:

(1...5).mod(1) // 1
(1...5).mod(6) // 1

你的回答让我想知道为什么我们不能在SE上接受多个答案。喜欢你用Swift 2的例子,我会留着它以后使用,这使得编写我想要的内容变得非常容易,并且正如预期的那样工作!非常感谢! - Pop Flamingo

3

这里是代码。该函数接受您想要映射的数字以及范围的最低值和最高值,并返回范围内的值:

func mapNum(n: Int, low lo: Int, high hi: Int) -> Int {
    let spread = hi - lo + 1
    return ((n - lo) % spread + spread) % spread + lo
}

示例:

mapNum(-9, low: 0, high: 4)    // returns "1"
mapNum(-9, low: -3, high: 1)   // returns "1"
mapNum(1, low: -11, high: -7)  // returns "-9"
mapNum(3, low: -8, high: -4)   // returns "-7"
mapNum(-5, low: 4, high: 8)    // returns "5"
mapNum(-5, low: -6, high: -2)  // returns "-5"
mapNum(-9, low: -7, high: -3)  // returns "-4"

((n - lo) % spread + spread) % spread + lo((n - lo) % spread) + lo 是相同的。虽然解决方案是正确的! - H W
@HW,额外的 + spread % spread 是为了处理负数。在 Swift 中,与 C 不同,-7 % 2-1,而我们需要正余数,因此需要额外的“舞蹈”。我不使用条件语句,因为计算通常比分支快。 - vacawama
@vacawama 哇!你的函数以一种非常简单的方式完美地实现了我想要的功能,非常感谢。我会花时间去理解这些操作! - Pop Flamingo
哦,那就忽略我的评论吧 @vacawama - 我以为 Swift 使用“通常”的模运算。 - H W
1
@TrevörAnneDenise,该函数使用模运算符“%”来计算我们在范围内的距离,然后将其加到范围的下限上以获得最终答案。请参见我对@HW的评论,了解有关额外+ spread%spread的解释。 - vacawama

1
extension Int {
    var convertedNumber: Int {
        if self < 0 {
            return  self * -4  % 5 + 1
        }
        return ( self % 5 ) + 1
    }
}
let n = -9
let result = n.convertedNumber  // 2


println(result) // 2

(-9).convertedNumber  // 2
(-8).convertedNumber  // 3
(-7).convertedNumber  // 4
(-6).convertedNumber  // 5
(-5).convertedNumber  // 1
(-4).convertedNumber  // 2
(-3).convertedNumber  // 3
(-2).convertedNumber  // 4
(-1).convertedNumber  // 5
(0).convertedNumber     // 1
(1).convertedNumber     // 2
(2).convertedNumber     // 3
(3).convertedNumber     // 4
(4).convertedNumber     // 5
(5).convertedNumber     // 1
(6).convertedNumber     // 2
(7).convertedNumber     // 3
(8).convertedNumber     // 4
(9).convertedNumber     // 5
(10).convertedNumber    // 1

扩展提供了一种有趣和现代的编写代码的方式。您的代码几乎做到了我想要的,因为实际上,在我的例子中,-1 应该是 5,而不是 4。但还是谢谢您提供的示例。 - Pop Flamingo
@TrevörAnneDenise,你只需要将结果加1即可。 - Leo Dabus

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