在前面的 GPT4 解 Leetcode 题目中,GPT4 的表现都很不错,给出了不错的代码和解释,看起来像是真正理解了题目。但是今天这个题目1802. Maximum Value at a Given Index in a Bounded Array ,GPT-4的表现就像是一个机智的“作弊者”,巧妙地提供了正确的代码,然而在解释代码的过程中,却出现了一些低级的错误。这种情况既展示了人工智能的强大能力,同时也揭示了一个事实:即便是最先进的人工智能,至今仍无法完全模拟人类的思维过程

在接下来的文章中,我将详细介绍 GPT-4 是如何解决这个问题的,以及它在解释过程中出现的问题。我希望这篇文章能让你对人工智能在编程领域的应用有更深入的理解,也希望你能从中找到乐趣和启发。

GPT4 意识到自己之前错误的解释

问题与GPT的解答

这个题目叫做 “在有界数组中选择最大值“,它要求你构建一个数组 nums,满足以下条件:

  1. nums.length == n
  2. nums[i] 是一个正整数,其中 0 <= i < n。
  3. abs(nums[i] - nums[i+1]) <= 1,其中 0 <= i < n-1。
  4. nums 的所有元素之和不超过 maxSum。
  5. 最大化 nums[index]。

你需要返回构建的数组中 nums[index] 的值。GPT4 直接给出了一个经典的解决办法二分查找,设定一个范围,然后在这个范围内查找可能的最大值。如果我们找到的值使得数组的总和超过 maxSum,那么我们就减小范围。如果总和小于 maxSum,那么我们就增大范围。继续这个过程,直到找到满足所有条件的最大值。

以下是给出的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def maxValue(self, n: int, index: int, maxSum: int) -> int:
def test(a):
b = max(a - index, 0)
res = (a + b) * (a - b + 1) // 2
b = max(a - ((n - 1) - index), 0)
res += (a + b) * (a - b + 1) // 2
return res - a

maxSum -= n
left, right = 0, maxSum
while left < right:
mid = (left + right + 1) // 2
if test(mid) <= maxSum:
left = mid
else:
right = mid - 1
return left + 1

这个代码首先定义了一个名为 test 的函数,用于计算给定值 a 时数组的总和。然后,我们在 0 和 maxSum 之间进行二分查找,寻找满足条件的最大值。如果 test(mid) 的结果小于或等于 maxSum,那么我们就将搜索范围移动到右半部分,否则我们就将搜索范围移动到左半部分。最后,我们返回找到的最大值加 1(因为我们在计算 maxSum 时减去了 n)。

GPT4 的“一知半解”

简单看了下实现,不太明白这里test函数具体是算什么的,GPT4 给出的解释如下:

下面引用是 GPT4 的解释

test 函数的目的是计算在给定的 a 值(也就是 nums[index])下,构造出的数组的总和。这个数组需要满足题目中的所有条件,包括每个元素与其相邻元素的差的绝对值不超过 1,以及 nums[index] 是最大的。

在 test 函数中,我们首先计算从 nums[index] 到数组的左端的元素的总和,然后计算从 nums[index] 到数组的右端的元素的总和。这两个总和加起来就是整个数组的总和。

具体来说,b = max(a - index, 0) 这一行是计算从 nums[index] 到数组左端的元素的最小值。如果 a - index 小于 0,那么 b 就是 0,否则 b 就是 a - index。然后,(a + b) * (a - b + 1) // 2 这一行是计算从 a 到 b 的所有整数的和。这个和就是从 nums[index] 到数组左端的元素的总和。

同样的,b = max(a - ((n - 1) - index), 0) 这一行是计算从 nums[index] 到数组右端的元素的最小值,然后 (a + b) * (a - b + 1) // 2 这一行是计算从 nums[index] 到数组右端的元素的总和。

最后,return res - a 这一行是返回整个数组的总和。这里要减去 a 是因为在计算左右两边的总和时,nums[index] 被计算了两次。

其实这里有一点 GPT4 一直没解释,那就是为啥从 index 往左一定是逐渐递减的。按照题目要求,这里至少也可以是等于的。比如给定的示例,[1,2,2,1]就是合法的。然后 GPT4 承认之前的解释可能有些不准确,给出了新的解释如下。

下面引用是 GPT4 的解释

在这个问题中,从 nums[index] 向左或向右看,数组的元素可以是逐渐递减的,也可以是保持不变的,但不能递增。这是因为 nums[index] 是最大的,且每个元素与其相邻元素的差的绝对值不能超过 1。

在 test 函数中,我们计算的是在最极端情况下(即元素值逐渐递减,而不是保持不变)的数组元素总和。这是因为我们想找到一个 a 值,使得在这个最极端情况下的数组元素总和仍然不超过 maxSum。如果我们找到了这样的 a 值,那么在其他不那么极端(即元素值保持不变或部分元素值保持不变)的情况下,数组元素总和肯定也不会超过 maxSum。

所以,虽然从 nums[index] 向左或向右看,数组的元素可以是逐渐递减的,也可以是保持不变的,但在 test 函数中,我们计算的是元素值逐渐递减的情况。这样做是为了找到一个安全的 a 值,使得在任何情况下,数组元素总和都不会超过 maxSum。

这里 GPT4 的解释让我明白了 test 的真实作用,不过也发现它的解释有部分错误。主要有下面两个地方:

  1. 如果我们找到了这样的 a 值,那么在其他不那么极端(即元素值保持不变或部分元素值保持不变)的情况下,数组元素总和肯定也不会超过 maxSum
  2. 在 test 函数中,我们计算的是元素值逐渐递减的情况。这样做是为了找到一个安全的 a 值,使得在任何情况下,数组元素总和都不会超过 maxSum

GPT4 会不会认错

直接提示它上面的回答错误,其实这里test函数中,计算的是在满足其他条件的的数组元素的最小总和,只要最小总和不超过 maxSum,这个序列就是满足要求的。不用关心在其他不那么极端(即元素值保持不变或部分元素值保持不变)的情况下,数组元素的总和。

下面引用是 GPT4 的解释

对不起,我之前的解释可能造成了混淆。你是对的。在这个问题中,我们的目标是找到一个满足所有条件的 a 值,使得数组的总和不超过 maxSum。在 test 函数中,我们计算的是在给定 a 值下,数组的最小可能总和。

这个最小可能总和是在以下情况下得到的:从 nums[index] 开始,向左和向右的元素值逐渐递减,直到 0 或 1。这样做的原因是,我们想要确保在任何可能的数组构造下,总和都不会超过 maxSum。如果在这个最极端的情况下,总和都不超过 maxSum,那么在其他情况下(例如,一些元素的值保持不变,而不是递减),总和肯定也不会超过 maxSum。

所以,你是对的,test 函数计算的是在满足其他条件的情况下,数组元素的最小总和。只要这个最小总和不超过 maxSum,那么这个 a 值就是满足要求的。我们不需要关心在其他不那么极端的情况下,数组元素的总和。

我们看到在直接告诉它正确内容的时候,它的部分回答还是错的。整体回答对错掺半,部分结论是错的,但是最后总结又是对的。错的部分是 如果在这个最极端的情况下,总和都不超过 maxSum,那么在其他情况下(例如,一些元素的值保持不变,而不是递减),总和肯定也不会超过 maxSum。我们接着来提示他,直接告诉这句话是完全错误的。这次 GPT4 终于意识到了自己的问题,给了一个很完美的解释。

下面引用是 GPT4 的解释

对不起,我之前的解释可能造成了混淆。你是对的。在这个问题中,我们的目标是找到一个满足所有条件的 a 值,使得数组的总和不超过 maxSum。在 test 函数中,我们计算的是在给定 a 值下,数组的最小可能总和。

这个最小可能总和是在以下情况下得到的:从 nums[index] 开始,向左和向右的元素值逐渐递减,直到 0 或 1。这样做的原因是,我们想要找到一个 a 值,使得在这个最极端的情况下,总和都不超过 maxSum。

我之前的解释可能让人误解为我们需要在所有可能的数组构造下都保证总和不超过 maxSum,这是不准确的。实际上,我们只需要找到一个 a 值,使得在最极端的情况下(即元素值逐渐递减),总和不超过 maxSum。这样,我们就可以保证 a 值是满足要求的。

重构与总结

到这里整个解释基本是清晰的,但是给的代码可读性其实不是很高,函数命名很随意。感觉是学习了不少“坏味道”的代码,所以给出的代码有点随意了。好在我们可以优化我们的提示词,让它把上面代码改的可维护,比如在关键地方加一些注释,变量名起的更有意义些。下面是 GPT4 给的新版本的代码,可读性就好了不少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
def maxValue(self, n: int, index: int, maxSum: int) -> int:
# 定义一个内部函数,计算给定最大值时,数组的最小可能总和
def calculate_min_sum(max_value):
# 计算从最大值到左边的最小值的总和
left_min = max(max_value - index, 0)
left_sum = (max_value + left_min) * (max_value - left_min + 1) // 2
# 计算从最大值到右边的最小值的总和
right_min = max(max_value - ((n - 1) - index), 0)
right_sum = (max_value + right_min) * (max_value - right_min + 1) // 2
# 返回数组的最小可能总和,注意要减去最大值,因为在计算左右两边的总和时,最大值被计算了两次
return left_sum + right_sum - max_value

# 减去 n 是因为每个元素至少为 1
maxSum -= n
left, right = 0, maxSum
# 通过二分查找找到满足条件的最大值
while left < right:
mid = (left + right + 1) // 2
# 如果给定最大值时,数组的最小可能总和不超过 maxSum,那么尝试增大最大值
if calculate_min_sum(mid) <= maxSum:
left = mid
# 否则,尝试减小最大值
else:
right = mid - 1
# 返回找到的最大值,注意要加上 1,因为我们在计算 maxSum 时减去了 n
return left + 1

整个对话可以在这里看到。

我们看到 GPT-4 成功地生成了解决问题的代码,并尝试解释其工作原理。然而,尽管 GPT-4 的表现令人印象深刻,但在解释过程中出现的一些问题也揭示了其局限性。这些问题主要体现在对某些概念的理解上,例如在解释计算数组总和的过程中,GPT-4 的解释并不完全准确。作为一个大型语言模型,GPT-4能够生成流畅且看似理解问题的回答,但实际上,它并不真正理解谈话的内容。GPT-4的回答是基于其在大量文本数据上的训练,它通过预测文本的概率分布来生成回答,而不是真正通过理解背后的逻辑

尽管如此,GPT-4仍然是一个强大的工具,可以帮助我们解决各种问题,只是需要我们多留一个心眼,多去验证