You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#实现文本文件数据邻近值匹配与线性插值优化方案

实现带插值功能的高效数据查询函数

我有一个包含10000行数据的文本文件,内容格式示例如下:

<p>16500000000 -11.6560625775</p>
<p>16600000000 -11.99428283515</p>
<p>16700000000 -12.06410749998</p>
<p>16800000000 -11.81220239236</p>

需要编写一个C#函数,接收文件路径和第一列的目标数值,返回对应的第二列数值;若目标数值未在第一列中找到,则通过线性插值计算结果。例如:

  • 输入16600000000时返回-11.99428283515
  • 输入16650000000时需在16600000000与16700000000对应值间插值

目前已有无插值功能的代码,现寻求优化实现插值且保证高效的方案,现有代码如下:

public static int GetPw(string filePath, double Freq, out double power, out string errMsg)
{
    power = Double.MinValue;
    errMsg = "No error";
    try
    {
        string[] lines = File.ReadAllLines(filePath);
        var filteredLines = lines.Where(line => !line.Contains("#") && !line.Contains("!") && !line.Contains("IND"));
        var splitLines = filteredLines.Select(line => line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
        var validLines = splitLines.Where(parts => parts.Length >= 2);
        var sortedLines = validLines.OrderBy(parts => Math.Abs(float.Parse(parts[0]) - targetFrequency));
        string closestPower = sortedLines.Select(parts => parts[1]).FirstOrDefault();

        if (!Double.TryParse(closestPower, CultureInfo.InvariantCulture, out power))
        {
            power = Double.MinValue;
            throw new InvalidCastException($"{power} is not a valid power value");
        }
    }
    catch (Exception e)
    {
        errMsg=e.Message;
        return -1;
    }
    return 0;
}

优化思路与实现

现有代码的核心问题是每次查询都要全量排序,仅能返回最近值且无法插值。针对10000行数据的规模,要保证高效,核心逻辑是一次性解析排序数据,利用二分查找快速定位插值区间,具体方案如下:

1. 预解析与缓存(多次查询必备)

如果函数会被多次调用,将解析排序后的数据缓存到静态集合中,避免重复读取文件和解析计算;单次查询可跳过缓存,但解析排序仍需执行。

2. 高精度解析与排序

  • 用double类型解析第一列数值,避免float的精度丢失问题
  • 按第一列数值从小到大排序,为二分查找做准备

3. 二分查找定位区间

利用List<T>.BinarySearch快速定位目标值:

  • 找到精确匹配时直接返回对应第二列数值
  • 未找到时,通过返回的插入点计算出目标值所在的左右相邻数据点

4. 线性插值计算

找到左右相邻点(x1,y1)和(x2,y2)后,通过公式计算目标值x对应的y:
y = y1 + (y2 - y1) * (x - x1) / (x2 - x1)

完整优化代码

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

public static class DataQueryHelper
{
    // 缓存解析排序后的数据,避免重复IO与计算(多次查询时启用)
    private static List<(double Frequency, double Power)> _cachedData;

    public static int GetPw(string filePath, double targetFreq, out double power, out string errMsg)
    {
        power = double.MinValue;
        errMsg = "No error";
        try
        {
            // 加载并解析数据(未缓存时执行)
            _cachedData ??= File.ReadAllLines(filePath)
                .Where(line => !line.Contains("#") && !line.Contains("!") && !line.Contains("IND"))
                .Select(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
                .Where(parts => parts.Length >= 2)
                .Select(parts => 
                {
                    if (double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double freq) &&
                        double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double pwr))
                    {
                        return (Frequency: freq, Power: pwr);
                    }
                    return default;
                })
                .Where(item => !double.IsNaN(item.Frequency))
                .OrderBy(item => item.Frequency)
                .ToList();

            if (_cachedData.Count == 0)
            {
                throw new InvalidOperationException("文件中无有效数据");
            }

            // 处理边界情况:目标值小于最小频率
            if (targetFreq <= _cachedData[0].Frequency)
            {
                power = _cachedData[0].Power;
                return 0;
            }
            // 目标值大于最大频率
            if (targetFreq >= _cachedData[^1].Frequency)
            {
                power = _cachedData[^1].Power;
                return 0;
            }

            // 二分查找目标频率
            int index = _cachedData.BinarySearch((targetFreq, 0), Comparer<(double, double)>.Create((a, b) => a.Item1.CompareTo(b.Item1)));
            if (index >= 0)
            {
                // 找到精确匹配
                power = _cachedData[index].Power;
                return 0;
            }

            // 计算插入点,获取左右相邻数据
            int insertIndex = ~index;
            var leftPoint = _cachedData[insertIndex - 1];
            var rightPoint = _cachedData[insertIndex];

            // 线性插值计算结果
            power = leftPoint.Power + (rightPoint.Power - leftPoint.Power) * (targetFreq - leftPoint.Frequency) / (rightPoint.Frequency - leftPoint.Frequency);
            return 0;
        }
        catch (Exception e)
        {
            errMsg = e.Message;
            return -1;
        }
    }

    // 可选:清空缓存,用于文件更新后重新加载数据
    public static void ClearCache()
    {
        _cachedData = null;
    }
}

关键优化点说明

  • 性能提升:解析排序仅执行一次(缓存后),后续查询用二分查找,时间复杂度从O(n log n)降至O(log n)
  • 精度保障:全程使用double类型处理数值,避免精度丢失
  • 边界处理:针对目标值超出数据范围的场景,直接返回边界值,避免插值错误
  • 容错性:过滤解析失败的行,避免无效数据干扰结果

内容的提问来源于stack exchange,提问作者Markus.H1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:57:07