关于提示工程是否有用,有多大用,一直都没有一个定量的表述。下面是我在实际生活工作中的实际案例,验证提示工程的有效性。我是一个即将毕业的研究生,我会用GPT4编程、解决疑惑、写作、科研等。

编程

  1. 基于巧妙提示词的(一次询问,通过率100%)
    image
  2. 直接询问的(一次询问,通过率36%)
    5664125800e8c15579b1176b5aa6aad

-->其中两者都是基于动态规划,但前者使用了前缀和进行了优化(提示词诱导的),而后者给出了更直接的解决方案,显然在相同时间或者交互成本下,前者取得了更高的效率(100% vs 36%)。

一个额外的有趣的次数对比实验

这里试图通过直接询问,观察通过几次交互可以达到提示词(一次交互)的效果。需要注意的是这里有一个实验变量没控制好,就是这里的模型是claude-opus,也是目前Claude发布的最强模型,在社区中,编程能力得到不错的认可,多人认为超过gpt4,所以这里也可一定程度弥补实验变量不一致带来的不可靠性。实验结果是需要两次交互(交互效率降低一倍),这是在已知正确答案的情况下继续追问获得的,通常谁会这么做呢?

附录

  1. 每篇分享的点赞量都为正数,且不超过m。
  2. 第i篇分享的点赞量和第i+1篇分享的点赞量的大小关系。
    他想知道,在已知这些信息的前提下,所有分享的点赞量有多少种不同的可能。
    (结果可能很大,请输出其对 1000000007 取模的值)

<response_format>

问题解释:

$problem_explanation

解决方案概述:

$solution_outline

执行时间和复杂度分析:

$time_complexity_analysis

关键性能瓶颈候选:

$performance_bottlenecks

影响力和复杂度表格:

瓶颈 影响力 复杂度
$bottlenecks_table

瓶颈排序:

$bottlenecks_ordered

针对最高排名瓶颈的详细解释与代码重写:

详细解释

$top_bottleneck_explanation

代码重写

$top_bottleneck_code_rewrite

新代码存在的问题:

(仅在存在问题时包含)
$top_bottleneck_issues

问题解决尝试:

(仅在存在问题时包含)
$top_bottleneck_fix_attempt

针对第二高排名瓶颈的详细解释与代码重写:

详细解释

$second_bottleneck_explanation

代码重写

$second_bottleneck_code_rewrite

新代码存在的问题:

(仅在存在问题时包含)
$second_bottleneck_issues

问题解决尝试:

(仅在存在问题时包含)
$second_bottleneck_fix_attempt

针对第三高排名瓶颈的详细解释与代码重写:

详细解释

$third_bottleneck_explanation

代码重写

$third_bottleneck_code_rewrite

新代码存在的问题:

(仅在存在问题时包含)
$third_bottleneck_issues

问题解决尝试:

(仅在存在问题时包含)
$third_bottleneck_fix_attempt

完整代码重写与性能改进:

$full_code_with_improvements
</response_format>`

输入描述:
输入包含两行。
第一行两个正整数 n,m(1 ≤n,m≤2000),分别表示分享的总个数和每篇分享的点赞数的上限。
第二行一个长度为 n-1 的字符串 si,其中只包含">”,"="这三种字符。如果si='>',则第i篇分享的点赞量严格高于第 i+1篇,”<”和“=”同理。
输出描述
一行一个整数表示符合条件的所有分享的点赞量有多少种不同的可能,结果对 1000000007 取模。
样例输入
4 3
<=>
样例输出
5
提示
说明[1,2,2,1][2,3,3,2][1,3,3,1][2,3,3,1][1,3,3,2]一共五种可能

这是一个动态规划问题。我们可以用dp[i][j]表示前i篇分享,第i篇分享的点赞数为j的情况下的可能数量。然后根据第i篇和第i+1篇的大小关系来更新dp数组。

以下是C++代码实现:

#include
#include
#include

using namespace std;

const int MOD = 1000000007;

int main() {
int n, m;
string s;
cin >> n >> m >> s;

vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int j = 1; j <= m; j++) {
    dp[1][j] = 1;
}

for (int i = 2; i <= n; i++) {
    if (s[i-2] == '<') {
        for (int j = 1; j <= m; j++) {
            for (int k = 1; k < j; k++) {
                dp[i][j] = (dp[i][j] + dp[i-1][k]) % MOD;
            }
        }
    } else if (s[i-2] == '=') {
        for (int j = 1; j <= m; j++) {
            dp[i][j] = dp[i-1][j];
        }
    } else {
        for (int j = 1; j <= m; j++) {
            for (int k = j + 1; k <= m; k++) {
                dp[i][j] = (dp[i][j] + dp[i-1][k]) % MOD;
            }
        }
    }
}

int ans = 0;
for (int j = 1; j <= m; j++) {
    ans = (ans + dp[n][j]) % MOD;
}
cout << ans << endl;

return 0;

}
解释:

我们用二维数组dp,其中dp[i][j]表示前i篇分享,第i篇分享的点赞数为j的情况下的可能数量。
初始化dp[1][j] = 1,表示第一篇分享的点赞数可以是1到m的任意值。
从第二篇分享开始,根据第i篇和第i+1篇的大小关系来更新dp数组:
如果s[i-2] == '<',说明第i篇分享的点赞数要小于第i-1篇,所以我们枚举第i篇的点赞数j,然后将第i-1篇点赞数小于j的所有情况累加到dp[i][j]上。
如果s[i-2] == '=',说明第i篇和第i-1篇的点赞数相等,直接将dp[i][j]赋值为dp[i-1][j]。
如果s[i-2] == '>',说明第i篇分享的点赞数要大于第i-1篇,类似地枚举第i篇的点赞数j,将第i-1篇点赞数大于j的所有情况累加到dp[i][j]上。
最后将dp[n][j]累加起来,就是所有可能的点赞数的总数。
时间复杂度为O(nm^2),空间复杂度为O(nm)。

用python实现时间效率最高的算法

下面是用Python实现的时间效率最高的算法:

MOD = 1000000007

def count_possibilities(n, m, s):
dp = [[0] * (m + 1) for _ in range(n + 1)]
for j in range(1, m + 1):
dp[1][j] = 1

for i in range(2, n + 1):
    if s[i-2] == '<':
        for j in range(1, m + 1):
            dp[i][j] = sum(dp[i-1][k] for k in range(1, j)) % MOD
    elif s[i-2] == '=':
        for j in range(1, m + 1):
            dp[i][j] = dp[i-1][j]
    else:
        for j in range(1, m + 1):
            dp[i][j] = sum(dp[i-1][k] for k in range(j + 1, m + 1)) % MOD

return sum(dp[n][j] for j in range(1, m + 1)) % MOD

n, m = map(int, input().split())
s = input()
print(count_possibilities(n, m, s))
解释:

我们使用与之前C++实现类似的动态规划方法,用二维数组dp,其中dp[i][j]表示前i篇分享,第i篇分享的点赞数为j的情况下的可能数量。
初始化dp[1][j] = 1,表示第一篇分享的点赞数可以是1到m的任意值。
从第二篇分享开始,根据第i篇和第i+1篇的大小关系来更新dp数组:
如果s[i-2] == '<',说明第i篇分享的点赞数要小于第i-1篇,所以我们枚举第i篇的点赞数j,然后将第i-1篇点赞数小于j的所有情况累加到dp[i][j]上。这里使用了Python的sum函数和生成器表达式来优化求和过程。
如果s[i-2] == '=',说明第i篇和第i-1篇的点赞数相等,直接将dp[i][j]赋值为dp[i-1][j]。
如果s[i-2] == '>',说明第i篇分享的点赞数要大于第i-1篇,类似地枚举第i篇的点赞数j,将第i-1篇点赞数大于j的所有情况累加到dp[i][j]上。同样使用了sum函数和生成器表达式。
最后使用sum函数和生成器表达式将dp[n][j]累加起来,就是所有可能的点赞数的总数。
这个Python实现利用了sum函数和生成器表达式来优化求和过程,避免了显式的嵌套循环,提高了时间效率。

时间复杂度仍为O(nm^2),空间复杂度为O(nm)。

这是效率最高的办法吗

你提出了一个很好的问题。我们可以进一步优化算法,将时间复杂度降低到O(n*m)。

优化的思路是,我们可以预处理出前缀和数组,然后在更新dp数组时,直接使用前缀和来计算区间和,避免了内层循环。

下面是优化后的Python代码:

MOD = 1000000007

def count_possibilities(n, m, s):
dp = [[0] * (m + 1) for _ in range(n + 1)]
for j in range(1, m + 1):
dp[1][j] = 1

for i in range(2, n + 1):
    prefix_sum = [0] * (m + 2)
    for j in range(1, m + 1):
        prefix_sum[j + 1] = (prefix_sum[j] + dp[i-1][j]) % MOD

    if s[i-2] == '<':
        for j in range(1, m + 1):
            dp[i][j] = prefix_sum[j]
    elif s[i-2] == '=':
        for j in range(1, m + 1):
            dp[i][j] = dp[i-1][j]
    else:
        for j in range(1, m + 1):
            dp[i][j] = (prefix_sum[m + 1] - prefix_sum[j + 1]) % MOD

return sum(dp[n][j] for j in range(1, m + 1)) % MOD

n, m = map(int, input().split())
s = input()
print(count_possibilities(n, m, s))
解释:

我们在每次更新dp数组之前,先计算出前缀和数组prefix_sum。prefix_sum[j]表示dp[i-1][1]到dp[i-1][j-1]的和。
当s[i-2] == '<'时,我们直接将prefix_sum[j]赋值给dp[i][j],因为dp[i][j]的值就是dp[i-1][1]到dp[i-1][j-1]的和。
当s[i-2] == '='时,更新方式不变,直接将dp[i-1][j]赋值给dp[i][j]。
当s[i-2] == '>'时,我们将prefix_sum[m + 1]减去prefix_sum[j + 1],得到dp[i-1][j+1]到dp[i-1][m]的和,赋值给dp[i][j]。
最后的求和过程不变。
通过引入前缀和数组,我们避免了内层循环,将时间复杂度降低到了O(n*m)。

空间复杂度仍为O(n*m),但我们可以进一步优化空间复杂度到O(m),因为在更新dp数组时,我们只需要用到上一行的数据。你可以尝试自己实现空间优化的版本。