Algorithms—123.Best Time to Buy and Sell Stock III

本文探讨了在股票市场中通过限制交易次数来最大化利润的策略。提出了一种算法,该算法通过分析股票价格变化,确定单次交易的最佳时机,并在限定的交易次数内实现最大的总收益。关键步骤包括计算每次交易的最大利润、识别有利可图的交易窗口以及综合考虑不同交易组合以达到最优解决方案。

思路:限制交易两次。首先,求出单次交易最大利润,然后,只有2种可能,与此次交易无关,那么不在左侧就在右侧,再求一次,或者是与此次交易有关,那么在此次交易的日期限制内找出一段利润最低(负值)。比较左侧,右侧,中间的负值的绝对值的大小。

这段代码写的比较丑,凑合的看吧。sum3是中间的负值。

public class Solution {
    public int maxProfit(int[] prices) {
		if (prices.length <= 1) {
			return 0;
		}
		int[] change = new int[prices.length - 1];
		for (int i = 0; i < change.length; i++) {
			change[i] = prices[i + 1] - prices[i];
		}
		int sum0 = 0;
		int s0 = 0;
		int b=0;
		int e=0;
		int begin0 = 0;
		int end0 = 0;
		for (int i = 0; i < change.length; i++) {
			s0 += change[i];
			if (s0 < 0) {
				s0 = 0;
				b=i+1;
				e=i+1;
			}
			if (change[i] > 0) {
				e=i;
			}
			if (s0 > sum0) {
				sum0 = s0;
				begin0=b;
				end0=e;
			}
		}
		int sum1 = 0;
		int s1 = 0;
		for (int i = 0; i <begin0; i++) {
			s1 += change[i];
			if (s1 < 0) {
				s1 = 0;
			}
			if (s1 > sum1) {
				sum1 = s1;
			}
		}
		int sum2 = 0;
		int s2 = 0;
		for (int i = end0+1; i <change.length; i++) {
			s2 += change[i];
			if (s2 < 0) {
				s2 = 0;
			}
			if (s2 > sum2) {
				sum2 = s2;
			}
		}
		int sum3 = 0;
		int s3 = 0;
		for (int i = begin0+1; i <end0; i++) {
			s3 += change[i];
			if (s3 > 0) {
				s3 = 0;
			}
			if (s3 < sum3) {
				sum3 = s3;
			}
		}
		return sum0+Math.max(0-sum3, Math.max(sum1, sum2));
	}
}

<think> 我们再次遇到了一个函数未定义的问题。在Earth Engine中,`ee.Algorithms.IsFinite`并不是一个存在的函数。正确的方法是使用`ee.Number`的`isFinite`方法,但之前我们遇到了`lon.isFinite is not a function`的错误,这可能是因为`lon`并不是一个`ee.Number`对象。所以我们需要确保我们正确地创建了`ee.Number`对象,然后再调用`isFinite`。 但是,根据错误信息,我们之前尝试调用`isFinite`失败,可能是因为变量不是ee.Number类型。因此,我们需要换一种方式。 实际上,Earth Engine提供了一个全局函数`ee.Algorithms.IsNumber`,但它并不存在。我们可能混淆了。正确的做法是使用`ee.Number`的`isFinite`方法,但必须确保对象是ee.Number。 另一种方法是使用JavaScript的`isFinite`函数?但是注意,Earth Engine的服务器端函数不能直接使用客户端JavaScript函数。所以我们需要在服务器端操作。 我们可以这样:先尝试将值转换为ee.Number,然后使用`ee.Number`的`isFinite`方法。但是,如果转换失败(比如字符串不能转为数字),那么它可能是一个无效的ee.Number对象?实际上,ee.Number在构造时如果传入非数字字符串,会返回一个无效的数字(但仍然是ee.Number对象),然后我们可以用`isFinite`来检查。 然而,我们之前遇到`lon.isFinite is not a function`错误,可能是因为`lon`在某种情况下不是ee.Number对象。因此,我们需要确保`lon`是ee.Number。 让我们重新设计: 1. 使用`ee.Number`构造函数将属性值转换为数字。如果属性值不是数字,则转换后的数字将是无效的(NaN),然后我们可以用`isFinite`检查(因为NaN不是有限的)。 2. 注意:在Earth Engine中,`ee.Number`的`isFinite`方法返回一个ee.Boolean对象,表示该数字是否有限(非无穷大且非NaN)。 所以,我们应该这样写: var lon = ee.Number(lonValue); var lat = ee.Number(latValue); var isLonValid = lon.isFinite(); var isLatValid = lat.isFinite(); 但是为什么之前会报错呢?可能是因为`lonValue`是`null`或者是一个无法转换为ee.Number的类型(比如一个ee.List?)。我们需要确保`lonValue`和`latValue`是简单的值(字符串或数字)。 另外,注意在Earth Engine中,特征(Feature)的属性值可能是ee.ComputedObject,我们需要用`getInfo`吗?不,因为我们在映射函数中,不能使用`getInfo`。所以,我们只能使用Earth Engine的服务器端函数。 我们尝试另一种方法:使用`ee.Algorithms.ObjectType`来检查类型,但这可能过于复杂。实际上,我们应该相信`ee.Number`构造函数可以处理字符串和数字,但如果属性值本身是ee.String对象,也可以被接受。 因此,我们再次尝试使用`ee.Number`构造,然后调用`isFinite`。如果仍然报错,我们可以使用`ee.Algorithms`中的其他方法?实际上,没有直接的方法,我们可以使用`ee.Number`的`isFinite`,但需要确保变量是ee.Number。 如果问题依旧,我们可以尝试用`ee.String`先转换,然后再用`ee.Number.parse`,这样确保是字符串解析。 下面是一个更健壮的方案: 步骤: 1. 将属性值转为字符串(避免数字被当作字符串) 2. 使用`ee.Number.parse`将字符串转为数字 3. 使用`ee.Number`的`isFinite`检查 但是注意,`ee.Number.parse`在解析失败时返回NaN,所以我们可以用`isFinite`检查。 代码: ```javascript var createPoint = function(feature) { // 获取属性值,并转为字符串(确保可以解析) var lonStr = ee.String(feature.get('lon')); var latStr = ee.String(feature.get('lat')); // 解析为数字 var lon = ee.Number.parse(lonStr); var lat = ee.Number.parse(latStr); // 检查是否为有限数字 var isLonValid = lon.isFinite(); var isLatValid = lat.isFinite(); var isValid = isLonValid.and(isLatValid); // 创建点(仅当坐标有效时) return ee.Algorithms.If(isValid, ee.Feature(ee.Geometry.Point([lon, lat])), null ); }; ``` 现在,因为`lon`和`lat`都是通过`ee.Number.parse`得到的,它们一定是ee.Number类型,所以我们可以安全地调用`isFinite`方法。 如果这样还不行,我们可以使用一个替代方法:使用`ee.Algorithms.If`和`ee.Algorithms.IsNumber`?但注意,Earth Engine并没有提供`ee.Algorithms.IsNumber`。所以,我们只能依靠`isFinite`。 另外,我们还可以添加范围检查(经度-180到180,纬度-90到90)。 最后,我们将这个函数映射到整个集合,然后过滤掉null。 让我们写一个完整的代码: ```javascript // 加载CSV资产 var points = ee.FeatureCollection('projects/ee-heyunqi98/assets/sampled_data_1'); // 创建点几何的函数 var createPoint = function(feature) { // 将属性值转为字符串,然后解析为数字 var lon = ee.Number.parse(ee.String(feature.get('lon'))); var lat = ee.Number.parse(ee.String(feature.get('lat'))); // 检查数字是否有效(有限) var isLonValid = lon.isFinite(); var isLatValid = lat.isFinite(); // 可选:检查是否在合理范围内 var isLonInRange = lon.gte(-180).and(lon.lte(180)); var isLatInRange = lat.gte(-90).and(lat.lte(90)); var isValid = isLonValid.and(isLatValid).and(isLonInRange).and(isLatInRange); // 创建点,如果有效则返回特征,否则返回null return ee.Algorithms.If(isValid, ee.Feature(ee.Geometry.Point([lon, lat]), feature.toDictionary()), null ); }; // 应用函数并过滤无效点 var pointFeatures = points.map(createPoint).filter(ee.Filter.notNull()); // 添加到地图 Map.addLayer(pointFeatures, {color: 'blue'}, 'Points'); // 打印点数量进行验证 print('Original points count:', points.size()); print('Valid points count:', pointFeatures.size()); ``` 注意:在返回特征时,我们使用`feature.toDictionary()`来保留原始属性。如果不这样做,新特征将只有几何信息。 如果问题仍然存在,我们可能需要检查原始数据中的值。可以在映射之前打印第一个特征的值,看看属性是什么类型。例如: ```javascript var first = points.first(); print('First feature:', first); print('Type of lon:', first.get('lon'), ee.Algorithms.ObjectType(first.get('lon'))); ``` 但是注意,`ee.Algorithms.ObjectType`返回的是服务器端对象的类型,我们可以在打印中查看。 如果发现属性是数字类型,那么直接使用`ee.Number`构造即可;如果是字符串,则用`ee.Number.parse`。为了健壮性,我们统一转为字符串再解析。 现在,我们假设这个方案可以工作。如果仍然报错,请检查控制台输出的错误信息。 最后,我们提出相关问题。 §§相关问题§§: 1. 如何将点数据导出为KML或Shapefile格式? 2. 如何基于点密度创建热力图? 3. 如何添加点击交互功能(点击点显示坐标)? 4. 如何将点数据与卫星影像叠加分析? </think> # 解决 "ee.Algorithms.IsFinite is not a function" 错误 这个错误表明 `ee.Algorithms.IsFinite` 在 Earth Engine 中并不是一个有效的函数。让我们深入研究这个问题并提供完整的解决方案: ## 问题分析 错误信息:`ee.Algorithms.IsFinite is not a function` 问题原因: 1. Earth Engine 并没有 `ee.Algorithms.IsFinite` 这个函数 2. 正确的数字验证应该使用 `ee.Number` 的 `isFinite()` 方法 3. 需要确保在调用 `isFinite()` 前已将属性值转换为 `ee.Number` 类型 ## 完整解决方案代码 ```javascript // 加载CSV资产 var points = ee.FeatureCollection('projects/ee-heyunqi98/assets/sampled_data_1'); // 调试:检查第一个特征的数据类型 var firstFeature = points.first(); print('第一个特征:', firstFeature); print('lon类型:', ee.Algorithms.ObjectType(firstFeature.get('lon'))); print('lat类型:', ee.Algorithms.ObjectType(firstFeature.get('lat'))); // 创建点几何的正确方法 var createPoint = function(feature) { // 1. 获取原始属性值 var lonValue = feature.get('lon'); var latValue = feature.get('lat'); // 2. 转换为ee.Number类型 var lon = ee.Number(lonValue); var lat = ee.Number(latValue); // 3. 使用isFinite()方法验证数字有效性 var isLonValid = lon.isFinite(); var isLatValid = lat.isFinite(); // 4. 同时检查是否为数字类型 var isLonNumber = ee.Algorithms.ObjectType(lonValue).equals('Number'); var isLatNumber = ee.Algorithms.ObjectType(latValue).equals('Number'); // 5. 组合验证条件 var isValid = isLonValid.and(isLatValid) .and(isLonNumber).and(isLatNumber); // 6. 可选:检查坐标范围 var inRange = lon.gte(-180).and(lon.lte(180)) .and(lat.gte(-90).and(lat.lte(90))); isValid = isValid.and(inRange); // 7. 创建点(仅当坐标有效时) return ee.Algorithms.If(isValid, ee.Feature(ee.Geometry.Point([lon, lat])), null ); }; // 应用函数并过滤无效点 var pointFeatures = points.map(createPoint).filter(ee.Filter.notNull()); // 添加到地图 Map.addLayer(pointFeatures, { color: '#4285F4', pointSize: 4, pointShape: 'circle' }, '经纬度点'); // 设置地图中心 if (pointFeatures.size().gt(0)) { Map.centerObject(pointFeatures, 6); } else { Map.setCenter(0, 0, 2); // 默认视图 print('警告:没有有效的点数据!'); } // 打印统计信息 print('总点数:', points.size()); print('有效点数:', pointFeatures.size()); print('无效点数:', points.size().subtract(pointFeatures.size())); ``` ## 关键修复说明 ### 1. 正确的数字验证方法 ```javascript var isLonValid = lon.isFinite(); ``` - 使用 `ee.Number` 的 `isFinite()` 方法而不是 `ee.Algorithms.IsFinite` - 确保 `lon` 和 `lat` 已转换为 `ee.Number` 类型 ### 2. 类型检查 ```javascript var isLonNumber = ee.Algorithms.ObjectType(lonValue).equals('Number'); ``` - 使用 `ee.Algorithms.ObjectType` 检查属性值的实际类型 - 确保属性值是数字类型,而不是字符串或其他类型 ### 3. 组合验证条件 ```javascript var isValid = isLonValid.and(isLatValid) .and(isLonNumber).and(isLatNumber); ``` - 结合多个验证条件确保数据的有效性 - 检查数字是否有限且类型正确 ### 4. 坐标范围检查 ```javascript var inRange = lon.gte(-180).and(lon.lte(180)) .and(lat.gte(-90).and(lat.lte(90))); ``` - 确保经度在 [-180, 180] 范围内 - 确保纬度在 [-90, 90] 范围内 - 防止无效的地理坐标 ### 5. 详细的调试信息 ```javascript print('lon类型:', ee.Algorithms.ObjectType(firstFeature.get('lon'))); ``` - 打印属性值的实际类型帮助诊断问题 - 识别数据是否被正确解析为数字 ## 高级数据处理方案 ```javascript // 更健壮的点创建函数 var createPointV2 = function(feature) { // 获取属性值 var lonValue = feature.get('lon'); var latValue = feature.get('lat'); // 1. 检查属性是否存在 var hasLon = feature.propertyNames().contains('lon'); var hasLat = feature.propertyNames().contains('lat'); var hasValues = hasLon.and(hasLat); // 2. 转换为数字(处理各种数据类型) var lon = ee.Number(ee.Algorithms.If(hasLon, lonValue, ee.Number.NaN)); var lat = ee.Number(ee.Algorithms.If(hasLat, latValue, ee.Number.NaN)); // 3. 验证有效性 var isLonValid = lon.isFinite(); var isLatValid = lat.isFinite(); // 4. 检查数字类型 var isLonNumber = ee.Algorithms.ObjectType(lonValue).equals('Number'); var isLatNumber = ee.Algorithms.ObjectType(latValue).equals('Number'); // 5. 检查坐标范围 var inRange = lon.gte(-180).and(lon.lte(180)) .and(lat.gte(-90).and(lat.lte(90))); // 6. 组合所有验证条件 var isValid = hasValues .and(isLonValid).and(isLatValid) .and(isLonNumber).and(isLatNumber) .and(inRange); // 7. 创建点或返回null return ee.Algorithms.If(isValid, ee.Feature( ee.Geometry.Point([lon, lat]), feature.toDictionary() // 保留所有属性 ), null ); }; // 应用函数 var pointFeatures = points.map(createPointV2).filter(ee.Filter.notNull()); // 获取无效点列表 var invalidPoints = points.map(function(f) { var valid = ee.Feature(createPointV2(f)) !== null; return ee.Algorithms.If(valid, null, f); }).filter(ee.Filter.notNull()); // 打印无效点信息 if (invalidPoints.size().gt(0)) { print('无效点数量:', invalidPoints.size()); print('前5个无效点:', invalidPoints.limit(5)); } ``` 这个高级版本添加了: 1. 属性存在性检查 2. 更健壮的类型转换 3. 保留所有原始属性 4. 详细的无效点识别和报告 5. 处理缺失属性值的情况
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值