7

StackOverflow 人群。我有一个非常开放的软件设计问题。

一段时间以来,我一直在寻找一个优雅的解决方案,我想知道这里是否有人对这个问题有一些出色的洞察力。认为这就像一个数据结构难题。

我要做的是创建一个能够从任何单位转换为任何单位的单位转换器。假设词法分析和解析已经完成。几个简单的例子:

Convert("days","hours")           // Yields 24
Convert("revolutions", "degrees") // Yields 360

为了让事情变得更复杂一点,它必须平滑地处理输入之间的歧义:

Convert("minutes","hours")        // Yields (1/60)
Convert("minutes","revolutions")  // Yields (1/21600)

为了让事情变得更有趣,它必须处理复杂的单元而不需要列举所有的可能性:

Convert("meters/second","kilometers/hour")
Convert("miles/hour","knots")
Convert("Newton meters","foot pounds")
Convert("Acre feet","meters^3")

没有正确或错误的答案,我正在寻找有关如何完成此任务的想法。总是有一个蛮力解决方案,但我想要一些简单且可扩展的优雅

4

6 回答 6

3

我将从一个哈希表(或持久查找表 - 您选择如何实现)开始,该表在您希望放入的尽可能多的对之间进行单位转换。如果您放入每一个可能的对,那么这就是您的蛮力方法。

如果您只有部分对,则可以在必须找到组合的对中进行搜索。例如,假设我的哈希表中有这两个条目:

Feet|Inches|1/12
Inches|Centimeters|2.54

现在,如果我想将英尺转换为厘米,我有一个简单的图形搜索:顶点是英尺、英寸和厘米,边是 1/12 和 2.54 转换因子。这种情况下的解决方案是两条边 1/12, 2.54(当然是通过乘法组合)。如果您愿意,您可以对图形参数更加感兴趣。

另一种方法可能是应用溯因推理 - 为此查看有关代数问题解决者的 AI 文本......

编辑:寻址复合单元

简化问题:将“英亩”转换为“米^2”

在这种情况下,关键是理解我们在谈论长度单位,那么为什么我们不在表格中插入一个新列用于unit type,可以是“长度”或“面积”。即使在较早的情况下,这也将有助于提高性能,因为它为您提供了一个简单的列来减少搜索空间。

现在的诀窍是理解长度^2 = 面积。为什么不添加另一个存储此元数据的查找:

Area|Length|Length|*

我们将其与主要单位表相结合:

Meters|Feet|3.28|Length
Acres|Feet^2|43560|Area

所以算法是这样的:

  • 解是m^2,也就是m * m,就是一个length * length。
  • 输入是英亩,即一个面积。
  • 在元表中搜索 m,并找到长度 * 长度映射。请注意,在更复杂的示例中,可能存在不止一个有效映射。
  • 将转换 Acres->Feet^2 添加到解决方案中。
  • 对 Feet->M 执行原始图形搜索。

注意:

  • 该算法不知道是使用面积还是长度作为工作的基本域。你可以给它提示,或者让它搜索两个空间。
  • 元表有点暴力。
  • 如果您开始混合类型(例如电阻 = 电压 / 电流)或做一些非常丑陋的事情并混合单位系统(例如 FooArea = 米 * 英尺),元表将需要变得更智能。
于 2010-07-06T21:37:11.580 回答
2

无论您选择什么结构,并且您的选择很可能由您的首选实现(OO?功能?DBMS 表?)指导,我认为您需要识别单元本身的结构。

例如,1000 公里/小时的测量有几个组成部分:

  • 标量大小,1000;
  • 一个前缀,在这种情况下是公斤;和
  • 一个维度,在本例中为 LT^(-1),即长度除以时间。

您的单位测量建模至少需要捕捉到这种复杂性。

正如已经建议的那样,您应该确定要使用的基本单位集是什么,并且国际单位制基本单位会立即提出建议。然后,您的建模单位数据结构将根据这些基本单位进行定义。因此,您可能会定义一个表(此处考虑 RDBMS,但可以轻松转换为您的首选实现),其中包含以下条目:

unit name      dimension                 conversion to base

foot           Length                    0.3048
gallon(UK)     Length^3                  4.546092 x 10^(-3)
kilowatt-hour  Mass.Length^2.Time^(-2)   3.6 x 10^6

等等。您还需要一个表格来将前缀(千、纳米、兆、米比等)转换为乘数,以及每个尺寸的基本单位表(即米是长度的基本单位,秒时间等)。您还必须处理单位,例如feet它们只是其他单位的同义词。

维度的目的当然是确保您的转换和其他操作(例如添加2 feet3.5 metres)是相称的。

而且,为了进一步阅读,我推荐Cardarelli 的这本书

编辑以回应评论......

我试图摆脱建议(特定于实现)的解决方案,所以我会多说一些。复合单位,例如千瓦时,确实会造成问题。一种方法是使用多个单位表达式标记测量值,例如kilowattandhour和组合它们的规则,在这种情况下,multiplication 我可以很快看到这变得非常棘手。将有效的单元集限制为应用程序域中最常见的单元可能会更好。

至于处理混合单位的测量,定义一个单位的维度的目的是提供一些方法来确保只有合理的操作可以应用于单位测量。因此,将两个长度 (L+L) 加在一起是明智的,但不是长度 (L) 和体积 (L^3)。另一方面,将体积除以长度(以获得面积(L^2))是明智的。并且取决于应用程序来确定奇怪的单位,例如每平方米千瓦时是否有效。

最后,我链接到的书确实列举了所有的可能性,我猜大多数带有单元的合理应用程序只会实现一个选择。

于 2010-07-06T22:17:32.510 回答
0

我会首先为每个数量选择一个标准单位(例如,长度为米,力为牛顿等),然后将所有转换因子存储到该单位的表格中

然后从几天到几小时,例如,您找到每天秒数和每小时秒数的转换因子,然后将它们除以找到答案。

对于歧义,每个单位都可以与它测量的所有类型的数量相关联,并且要确定要进行哪种转换,您将采用这两组类型的交集(如果您留下 0 或多个会吐出一个错误)

于 2010-07-06T21:31:46.347 回答
0

我假设您希望将有关转换的数据保存在某种三元组(fstUnit, sndUnit, multiplier)中。

对于单个单位转换:使用一些哈希函数O(1)将单位结构更改为一个数字,然后将所有乘数放入一个矩阵中(您只需要记住右上部分,因为反射是相同的,但相反)。

对于复杂情况:示例 1. m/s 到 km/h。您检查矩阵中的 (m,km),然后检查 (s,h),然后将结果相乘。示例 2. m^3 到 km^3。你检查 (m,km) 并将它的三次方。

当然,当类型与字段和卷不匹配时,会出现一些错误。

于 2010-07-06T21:44:40.750 回答
0

您可以为 Units 创建一个类,该类采用转换因子和所有基本单位的指数(我建议为此使用公制单位,这会让您的生活更轻松)。例如在伪 Java 中:


public class Unit {
   public Unit(double factor, int meterExp, int secondExp, int kilogrammExp ... [other base units]) {
     ...
   }
}

//you need the speed in km/h (1 m/s is 3.6 km/h):
Unit kmPerH = new Unit(1 / 3.6, 1, -1, 0, ...)

于 2010-07-06T21:44:42.497 回答
0

我会有一个包含这些字段的表:

conversionID
fromUnit
toUnit
multiplier

不管你需要多少行来存储你想要支持的所有转换

如果要支持多步骤过程(华氏度到摄氏度),则需要与单位表建立一对多关系,例如称为 conversionStep,字段如下

conversionID
sequence
operator
value

如果您想存储一组转换但支持多步转换,例如存储

Feet|Inches|1/12
Inches|Centimeters|2.54

并支持从英尺转换为厘米,我会将转换计划存储在另一个表中,例如

conversionPlanID
startUnits
endUnits
via

你的行看起来像

1 | feet | centimeters | inches
于 2010-09-30T15:47:53.957 回答