每日算法 — 使用java实现冲浪:波浪物理模拟与最优起跳决策

一、问题背景

冲浪是一项依赖波浪动力的水上运动。冲浪者需要在合适的时机从波浪的底部起跳,借助波浪的推力完成滑行。本文将用Java模拟二维波浪的物理传播,并设计贪心算法计算冲浪者的最优起跳位置与时机,最大化滑行距离。

二、核心算法思路

2.1 波浪物理模型

将水面简化为一系列正弦波的叠加。每个波浪具有振幅、周期、波长和相位四个属性。水面高度函数为:

h(x, t) = Σ A_i * sin(2π * (x / λ_i - t / T_i) + φ_i)

其中 A_i 为振幅,λ_i 为波长,T_i 为周期,φ_i 为相位。

2.2 冲浪者运动模型

冲浪者在波浪斜面上获得推力。波浪斜面的坡度决定了加速度:

a = g * sin(θ)

其中 θ 为波面与水平面的夹角,g 为重力加速度。

2.3 最优起跳决策

冲浪者需要在波浪即将破碎前的”绿浪区”起跳。判断条件为:
– 波面坡度 θ 在 [5°, 45°] 之间
– 波浪高度 H 超过阈值 H_min
– 起跳位置距离浪顶有足够滑行距离

使用贪心策略:在每个时间步,冲浪者评估前方波浪状态,选择坡度最大且稳定的波浪起跳。

三、Java实现

import java.util.ArrayList;
import java.util.List;

/**
 * 冲浪波浪物理模拟与最优起跳决策
 */
public class SurfingSimulation {

    // 物理常量
    private static final double GRAVITY = 9.8;
    private static final double WATER_DENSITY = 1025;
    private static final double MIN_WAVE_HEIGHT = 0.5;
    private static final double MAX_SLOPE_ANGLE = Math.toRadians(45);
    private static final double MIN_SLOPE_ANGLE = Math.toRadians(5);

    /**
     * 波浪类:描述单个正弦波分量
     */
    static class Wave {
        double amplitude;   // 振幅 (m)
        double wavelength;  // 波长 (m)
        double period;      // 周期 (s)
        double phase;       // 初始相位 (rad)

        Wave(double amplitude, double wavelength, double period, double phase) {
            this.amplitude = amplitude;
            this.wavelength = wavelength;
            this.period = period;
            this.phase = phase;
        }

        /**
         * 计算位置x、时间t时的水面高度
         */
        double heightAt(double x, double t) {
            double k = 2 * Math.PI / wavelength;
            double omega = 2 * Math.PI / period;
            return amplitude * Math.sin(k * x - omega * t + phase);
        }

        /**
         * 计算位置x、时间t时的波面斜率(导数)
         */
        double slopeAt(double x, double t) {
            double k = 2 * Math.PI / wavelength;
            double omega = 2 * Math.PI / period;
            return amplitude * k * Math.cos(k * x - omega * t + phase);
        }
    }

    /**
     * 冲浪者类
     */
    static class Surfer {
        double positionX;   // 水平位置 (m)
        double velocityX;   // 水平速度 (m/s)
        double velocityY;   // 垂直速度 (m/s)
        boolean isRiding;   // 是否在滑行
        double totalDistance; // 总滑行距离

        Surfer(double startX) {
            this.positionX = startX;
            this.velocityX = 0;
            this.velocityY = 0;
            this.isRiding = false;
            this.totalDistance = 0;
        }

        /**
         * 更新冲浪者状态
         */
        void update(double slope, double dt) {
            if (isRiding) {
                // 沿波面下滑的加速度
                double acceleration = GRAVITY * Math.sin(Math.atan(slope));
                velocityX += acceleration * dt;
                positionX += velocityX * dt;
                totalDistance += Math.abs(velocityX * dt);
            }
        }
    }

    /**
     * 海洋类:管理所有波浪分量
     */
    static class Ocean {
        List<Wave> waves = new ArrayList<>();

        void addWave(Wave wave) {
            waves.add(wave);
        }

        /**
         * 计算合成水面高度
         */
        double totalHeight(double x, double t) {
            double sum = 0;
            for (Wave w : waves) {
                sum += w.heightAt(x, t);
            }
            return sum;
        }

        /**
         * 计算合成波面斜率
         */
        double totalSlope(double x, double t) {
            double sum = 0;
            for (Wave w : waves) {
                sum += w.slopeAt(x, t);
            }
            return sum;
        }
    }

    /**
     * 最优起跳决策器:贪心策略
     */
    static class TakeoffAdvisor {

        /**
         * 评估某位置某时刻的起跳质量
         * 返回质量分数 [0, 1],分数越高越适合起跳
         */
        double evaluateTakeoff(Ocean ocean, double x, double t) {
            double slope = ocean.totalSlope(x, t);
            double height = ocean.totalHeight(x, t);
            double angle = Math.atan(Math.abs(slope));

            // 坡度不在合适范围内
            if (angle < MIN_SLOPE_ANGLE || angle > MAX_SLOPE_ANGLE) {
                return 0;
            }

            // 波浪高度不足
            if (Math.abs(height) < MIN_WAVE_HEIGHT) {
                return 0;
            }

            // 计算质量分数:坡度适中且高度足够时分数最高
            double slopeScore = 1 - Math.abs(angle - Math.toRadians(20)) / Math.toRadians(25);
            double heightScore = Math.min(Math.abs(height) / 2.0, 1.0);

            return slopeScore * heightScore;
        }

        /**
         * 在搜索范围内寻找最优起跳位置
         */
        double findBestTakeoffSpot(Ocean ocean, double t, double searchStart, double searchEnd, double step) {
            double bestX = searchStart;
            double bestScore = 0;

            for (double x = searchStart; x <= searchEnd; x += step) {
                double score = evaluateTakeoff(ocean, x, t);
                if (score > bestScore) {
                    bestScore = score;
                    bestX = x;
                }
            }
            return bestX;
        }
    }

    /**
     * 运行完整模拟
     */
    public static void runSimulation() {
        // 创建海洋环境
        Ocean ocean = new Ocean();
        ocean.addWave(new Wave(1.2, 20, 8, 0));
        ocean.addWave(new Wave(0.6, 12, 5, Math.PI / 4));
        ocean.addWave(new Wave(0.3, 8, 3, Math.PI / 2));

        // 创建冲浪者
        Surfer surfer = new Surfer(0);
        TakeoffAdvisor advisor = new TakeoffAdvisor();

        double totalTime = 60;    // 模拟60秒
        double dt = 0.1;          // 时间步长0.1秒
        double searchRange = 100; // 搜索范围100米

        System.out.println("=== 冲浪波浪物理模拟 ===");
        System.out.printf("波浪数量: %d%n", ocean.waves.size());
        System.out.printf("模拟时长: %.1f秒%n%n", totalTime);

        for (double t = 0; t < totalTime; t += dt) {
            if (!surfer.isRiding) {
                // 寻找最优起跳位置
                double bestX = advisor.findBestTakeoffSpot(ocean, t, 0, searchRange, 0.5);
                double score = advisor.evaluateTakeoff(ocean, bestX, t);

                if (score > 0.5) {
                    surfer.positionX = bestX;
                    surfer.isRiding = true;
                    System.out.printf("[t=%.1fs] 起跳! 位置=%.2fm, 质量分数=%.3f%n", t, bestX, score);
                }
            } else {
                // 滑行中
                double slope = ocean.totalSlope(surfer.positionX, t);
                surfer.update(slope, dt);

                // 判断是否应该结束滑行(坡度太平或波浪破碎)
                double angle = Math.atan(Math.abs(slope));
                if (angle < Math.toRadians(3)) {
                    System.out.printf("[t=%.1fs] 滑行结束. 本次距离=%.2fm, 总距离=%.2fm%n",
                            t, surfer.totalDistance, surfer.totalDistance);
                    surfer.isRiding = false;
                    surfer.velocityX = 0;
                }
            }
        }

        System.out.printf("%n模拟结束. 总滑行距离: %.2f米%n", surfer.totalDistance);
    }

    public static void main(String[] args) {
        runSimulation();
    }
}

四、算法复杂度分析

  • 波浪高度计算:O(n),其中 n 为波浪分量数量
  • 最优起跳搜索:O(m * n),m 为搜索步数,n 为波浪数量
  • 整体模拟:O(T/dt * m * n),T 为总模拟时间

五、扩展思考

  1. 多冲浪者竞争:可引入多智能体系统,冲浪者之间竞争最优波浪
  2. 机器学习优化:用强化学习训练起跳策略,替代贪心规则
  3. 三维扩展:将模型扩展到三维空间,考虑洋流和风向的影响