启发式算法在PHP中对任何“类型”的技术测量进行排序

问题描述 投票:0回答:2

我不同的列表与相同维度的测量,但有点混合单位

“1米,200毫米,1英尺”

或者也许

“1°C,273 K”等。

现在我想按绝对顺序对它们进行排序

“200毫米,1英尺,1米”和“273千克,1°C”

我想知道这是否是一个已经解决的问题,因为我不想重新发明轮子。我担心,这可能是某种“购买PHP扩展”的问题,但我已经找到了一些有用的软件包:

https://github.com/PhpUnitsOfMeasure/php-units-of-measure可以在度量单位之间进行各种对话。

我已经创建了用于分隔单元和数字的代码。

那么我在想什么,将单位“暴力”到某个维度:

https://github.com/PhpUnitsOfMeasure/php-units-of-measure/tree/master/source/PhysicalQuantity

接下来,我可以选择第一个维度并将所有内容转换为第一个“主”SI单元并对其进行排序。

对?

php measurement
2个回答
2
投票

通常,您需要做的是将这些单位转换为一些常用的度量,但仅用于排序。

使用usort()和自定义回调函数。在回调中,进行转换以进行比较。

确保在返回结果时保留原始单位,否则会出现舍入错误。


0
投票

根据建议,这就是我提出的解决方案

public function testCompareLength()
{
    $this->assertLessThan(0, $this->objectDe->compareFunction('100 mm', '1 m'));
}

public function testCompareTemperature()
{
    $this->assertLessThan(0, $this->objectDe->compareFunction('1 K', '0 °C'));
    $this->assertGreaterThan(0, $this->objectDe->compareFunction('0 °C', '1 K'));
    $this->assertEquals(0, $this->objectDe->compareFunction('-273 °C', '0 K'));
}

/**
 * @param $numberString
 *
 * @return array
 */
public function parseNumber($numberString): array
{
    $values = preg_split('/(?<=[0-9.,])(?=[^0-9,.]+)/i', $numberString);

    $float = $values[0];
    $unit = $values[1] ?? '';

    $decPos = strpos($float, '.');
    if ($decPos === false) {
        $precision = 0;
    } else {
        $precision = strlen($float) - $decPos - 1;
    }

    return ['float' => $float, 'unit' => $unit, 'precision' => $precision];
}


private function heuristicMeasureFactory($measure)
{
    $prioritizedDimensions = [
        Temperature::class,
        Length::class,
    ];

    $unit = trim($measure['unit']);

    foreach ($prioritizedDimensions as $class) {
        foreach ($class::getUnitDefinitions() as $definition) {
            if ($definition->getName() == $unit) {
                return new $class($measure['float'], $unit);
            }
        }
    }

    // now process aliases
    foreach ($prioritizedDimensions as $class) {
        foreach ($class::getUnitDefinitions() as $definition) {
            foreach ($definition->aliases as $alias) {
                if ($alias == $unit) {
                    return new $class($measure['float'], $unit);
                }
            }
        }
    }

    return null; // NaN
}

/**
 * Sort apples and oranges -- kind of. Not.
 *
 * Compares two strings which represend a measurement of the same physical dimension
 */
public function compareFunction($a, $b)
{
    $definitions = Temperature::getUnitDefinitions();

    $aParsed = $this->parseNumber($a);
    $aVal = $this->heuristicMeasureFactory($aParsed);

    $bParsed = $this->parseNumber($b);
    $bVal = $this->heuristicMeasureFactory($bParsed);

    if ($aVal == null || $bVal == null) {
        return strnatcmp($aVal, $bVal); // fallback to string comparision
    }

    return bccomp($aVal->subtract($bVal)->toNativeUnit(), 0, 36);
}
© www.soinside.com 2019 - 2024. All rights reserved.