单位转换的良好数据结构?

时间:2010-07-06 21:23:27

标签: data-structures software-design

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

我一直在寻找一个很好的解决方案,我想知道这里是否有人对问题有一些了解。认为这就像一个数据结构难题。

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

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")

没有正确或错误的答案,我正在寻找有关如何实现这一目标的想法。总是有一个强力解决方案,但我想要一些简单且可扩展的优雅

6 个答案:

答案 0 :(得分:3)

我会从一个哈希表(或持久查找表 - 您选择的实施方式)开始,它可以在你想要的任意对之间进行单位转换。如果你放入所有可能的对,那么这就是你的蛮力方法

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

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

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

另一种方法可能是应用诱导性推理 - 查看有关代数问题求解器的AI文本......

编辑:寻址复合单位

简化问题:将“Acres”转换为“Meters ^ 2”

在这种情况下,键理解我们正在讨论长度单位,那么为什么我们不在表中为单位类型插入一个新列,它可以是“长度”或“区域”。即使在早期的情况下,这也有助于提高性能,因为它为您提供了一个简单的列,可以缩小您的搜索空间。

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

Area|Length|Length|*

我们将其与主要单位表联系起来:

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

所以算法是:

  • 溶液是m ^ 2,m * m,长度*长度。
  • 输入是英亩,这是一个区域。
  • 在元表中搜索m,找到长度*长度映射。请注意,在更复杂的示例中,可能存在多个有效映射。
  • 在解决方案中附加转换Acres-> Feet ^ 2。
  • 执行原始图表搜索Feet-> M。

请注意:

  • 该算法不知道是否使用区域或长度作为工作的基本域。您可以提供提示,或让它搜索两个空格。
  • 元表有点蛮力。
  • 如果您开始混合类型(例如,电阻=电压/电流)或做一些非常丑陋的事情并混合单位系统(例如FooArea = Meters * Feet),元表将需要更智能。

答案 1 :(得分:3)

无论您选择哪种结构,您的选择都可能由您首选的实施(OO?functional?DBMS表?)指导。我认为您需要确定单位本身的结构。

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

  • 标量,1000;
  • 一个前缀,在本例中为kilo;和
  • 维度,在这种情况下为L.T ^( - 1),即长度除以时间。

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

正如已经建议的那样,您应该确定要使用的基本单位集,并且SI基本单位立即建议自己。然后,您将根据这些基本单位定义建模单位的数据结构。因此,您可以使用以下条目定义一个表(在此处考虑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

等等。您还需要一个表格来将前缀(kilo-,nano-,mega-,mibi-等)转换为乘法因子,以及每个维度的基本单位表(即米是长度的基本单位,秒对于时间等)。您还必须处理feet等单位,这些单位只是其他单位的同义词。

维度的目的当然是确保您的转化和其他操作(例如将2 feet添加到3.5 metres)相称。

而且,为了进一步阅读,我建议this book by Cardarelli

编辑以回应评论...

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

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

最后,我链接到的书确实列举了所有可能性,我想最合理的单位应用程序只会实现选择。

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

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

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

对于复杂案例: 例1.m / s到km / h。你检查矩阵中的(m,km),然后检查(s,h),然后乘以结果。 例2.m ^ 3到km ^ 3。你检查(m,km)并将它带到第三个电源。

当然有些错误,当类型与字段和音量不匹配时。

答案 4 :(得分:0)

你可以为单位制作一个类,它取得转换因子和所有基本单位的指数(我建议使用公制单位,这样可以让你的生活更轻松)。例如。在Pseudo-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, ...)

答案 5 :(得分:0)

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

conversionID
fromUnit
toUnit
multiplier

您需要存储要支持的所有转化的行数

如果你想支持一个多步骤的过程(F到C),你需要一个与单位表的一对多关系,比如叫做conversionStep,有

之类的字段
conversionID
sequence
operator
value

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

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

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

conversionPlanID
startUnits
endUnits
via

你的行看起来像

1 | feet | centimeters | inches