AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77812297
Accepted
user366312
user366312
Asked: 2024-01-14 00:58:48 +0800 CST2024-01-14 00:58:48 +0800 CST 2024-01-14 00:58:48 +0800 CST

该函数无法检测两条曲线的交点

  • 772

下面的源代码应该找到两条曲线之间的交点。

但是,该函数无法检测交点。

我该如何修复它?

在此输入图像描述

using MathNet.Numerics.Interpolation;
using System.Collections.Generic;
using System.Linq;

public static Vec2 FindIntersectionOfTwoCurves(List<double> xList1, List<double> yList1,
                                               List<double> xList2, List<double> yList2)
{
    if (xList1 == null || yList1 == null || xList2 == null || yList2 == null ||
        xList1.Count != yList1.Count || xList2.Count != yList2.Count)
        return null;

    IInterpolation interpolation1 = Interpolate.Linear(xList1, yList1);
    IInterpolation interpolation2 = Interpolate.Linear(xList2, yList2);

    double lowerBound = Math.Max(xList1.Min(), xList2.Min());
    double upperBound = Math.Min(xList1.Max(), xList2.Max());

    double step = (upperBound - lowerBound) / 1000; // adjust the step size as needed
    for (double x = lowerBound; x <= upperBound; x += step)
    {
        double y1 = interpolation1.Interpolate(x);
        double y2 = interpolation2.Interpolate(x);

        if (Math.Abs(y1 - y2) < 1e-7)
        {
            return new Vec2(x, y1);
        }
    }

    return null;
}

public class Vec2
{
    public double X { get; set; }
    public double Y { get; set; }

    public Vec2(double x, double y)
    {
        X = x;
        Y = y;
    }
}
c#
  • 2 2 个回答
  • 87 Views

2 个回答

  • Voted
  1. Best Answer
    Ahmed AEK
    2024-01-14T01:54:39+08:002024-01-14T01:54:39+08:00

    由于您试图找到两条曲线与非连续一阶导数的交点,因此您应该使用二分法。

    一个快速编写的C#实现如下,二分查找的实现来自这里,如果是线性间隔的,二分查找可以用更简单的线性插值代替X。

    namespace ConsoleApp2
    {
        public class LinearInterpolator
        {
            private static int BinarySearch<T>(IList<T> list, T value)
            {
                if (list == null)
                    throw new ArgumentNullException("list");
                var comp = Comparer<T>.Default;
                int lo = 0, hi = list.Count - 1;
                while (lo < hi)
                {
                    int m = (hi + lo) / 2;  // this might overflow; be careful.
                    if (comp.Compare(list[m], value) < 0) lo = m + 1;
                    else hi = m - 1;
                }
                if (comp.Compare(list[lo], value) < 0) lo++;
                return lo;
            }
    
            private static int FindFirstIndexGreaterThanOrEqualTo<T>
                                      (List<T> sortedList, T key)
            {
                return BinarySearch(sortedList, key);
            }
    
            List<double> x_values;
            List<double> y_values;
            public LinearInterpolator(List<double> x, List<double> y)
            {
                // quick argsort
                List<int> indicies = x.AsEnumerable().Select((v,i) => new {obj = v, index = i}).OrderBy(c=> c.obj).Select(c => c.index).ToList();
                x_values = indicies.Select(i => x[i]).ToList();
                y_values = indicies.Select(i => y[i]).ToList();
            }
    
            public double Interpolate(double x)
            {
                int index = FindFirstIndexGreaterThanOrEqualTo(x_values, x);
                if (index == 0)
                {
                    return y_values[0];
                }
                double y1 = y_values[index-1];
                double y2 = y_values[index];
                double x1 = x_values[index-1];
                double x2 = x_values[index];
                return (x - x1) / (x2 - x1) * (y2 - y1) + y1;
            }
    
        }
    
        class IntersectionFinder
        {
            public static Nullable<(double,double)> FindIntersectionOfTwoCurves(List<double> xList1, List<double> yList1,
                                                       List<double> xList2, List<double> yList2)
            {
                if (xList1 == null || yList1 == null || xList2 == null || yList2 == null ||
                    xList1.Count != yList1.Count || xList2.Count != yList2.Count)
                    return null;
    
                LinearInterpolator interpolator1 = new LinearInterpolator(xList1, yList1);
                LinearInterpolator interpolator2 = new LinearInterpolator(xList2, yList2);
    
                double lowerBound = Math.Max(xList1.Min(), xList2.Min());
                double upperBound = Math.Min(xList1.Max(), xList2.Max());
    
                if (lowerBound > upperBound) // X axes don't overlap
                {
                    return null;
                }
    
                double diff_start = interpolator1.Interpolate(lowerBound) - interpolator2.Interpolate(lowerBound);
                double diff_end = interpolator1.Interpolate(upperBound) - interpolator2.Interpolate(upperBound);
    
                if ((diff_start > 0 && diff_end > 0) || (diff_start < 0 && diff_end < 0)) // intersection doesn't exist
                {
                    return null;
                }
    
                double mid = (lowerBound + upperBound) / 2;
                double diff_mid = interpolator1.Interpolate(mid) - interpolator2.Interpolate(mid);
    
                int iterations = 0;
                while (Math.Abs(diff_mid) > 1e-7)
                {
                    if (diff_start > diff_end) // list1 is higher
                    {
                        if (diff_mid > 0) // mid is also higher, intersection in right side
                        {
                            lowerBound = mid;
                            diff_start = diff_mid;
                        }
                        else // mid is lower, intersection in left side
                        {
                            upperBound = mid;
                            diff_end = diff_mid;
                        }
                    }
                    else // list 2 is higher
                    {
                        if (diff_mid < 0) 
                        {
                            lowerBound = mid;
                            diff_start = diff_mid;
                        }
                        else 
                        {
                            upperBound = mid;
                            diff_end = diff_mid;
                        }
                    }
                    mid = (lowerBound + upperBound) / 2;
                    diff_mid = interpolator1.Interpolate(mid) - interpolator2.Interpolate(mid);
                    iterations++;
                    if (iterations > 10000) // prevent infinite loop if Y is discontinuous
                    {
                        return null;
                    }
                }
    
                return new (mid, interpolator1.Interpolate(mid));
            }
        }
    
        internal class Program
        {
            static void Main(string[] args)
            {
                List<double> xList1 = [ 1, 1.5, 2, 3.5, 4 ];
                List<double> xList2 = [ 1, 2, 3, 4, 5 ];
                List<double> yList1 = [ 0, 2, 4, 6,  8 ];
                List<double> yList2 = [ 8, 6, 4, 2, 0 ];
                Console.WriteLine(IntersectionFinder.FindIntersectionOfTwoCurves(xList1, yList1, xList2, yList2).ToString());
            }
        }
    }
    
    (2.5999999940395355, 4.799999992052714)
    

    在此输入图像描述

    • 1
  2. Wyck
    2024-01-14T01:57:47+08:002024-01-14T01:57:47+08:00

    这个小小的改变可以解决你的问题。不要注意Math.Abs(y1 - y2)小于某个 epsilon(例如1e-7)。相反,请注意 的符号y1 - y2发生变化,以便它与之前的迭代不同。这就是所谓的寻找零交叉点。

    例如:

    int? prevSign = null;
    for (double x = lowerBound; x <= upperBound; x += step) {
        double y1 = interpolation1.Interpolate(x);
        double y2 = interpolation2.Interpolate(x);
        int sign = Math.Sign(y1 - y2);
        if (prevSign.HasValue) {
            if (sign != prevSign.Value) {
                return new Vec2(x, y1);
            }
        } else {
            prevSign = sign;
        }
    }
    
    • 1

相关问题

  • Polly DecorlatedJitterBackoffV2 - 如何计算完成所有重试所需的最长时间?

  • Wpf。在 ScrollViewer 中滚动 DataGrid

  • 我在使用 .NET MAUI MVVM 的游戏页面上获得的分数在其他页面上不可见。如何在本地设备中保存分数数据

  • 从 DataTemplate 内部将 TreeView 层次结构与 HierarchicalDataTemplate 结合使用

  • 如何改进 .NET 中的验证接口?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行?

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    何时应使用 std::inplace_vector 而不是 std::vector?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Martin Hope
    Aleksandr Dubinsky 为什么 InetAddress 上的 switch 模式匹配会失败,并出现“未涵盖所有可能的输入值”? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge 为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini 具有指定基础类型但没有枚举器的“枚举类”的用途是什么? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer 何时应使用 std::inplace_vector 而不是 std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB 为什么 GCC 生成有条件执行 SIMD 实现的代码? 2024-02-17 06:17:14 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve