一、问题背景
冲浪是一项依赖波浪动力的水上运动。冲浪者需要在合适的时机从波浪的底部起跳,借助波浪的推力完成滑行。本文将用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为总模拟时间
五、扩展思考
- 多冲浪者竞争:可引入多智能体系统,冲浪者之间竞争最优波浪
- 机器学习优化:用强化学习训练起跳策略,替代贪心规则
- 三维扩展:将模型扩展到三维空间,考虑洋流和风向的影响