AI编程工具常常表现的比我们直接去问AI问题要聪明的多,因为它们通常都不是简单的把问题和代码丢给大模型,而是针对问题场景设计相应的思维链(CoT)/思维树(ToT)等来充分发挥大模型的潜力。其实通过很简单的AI接口调用,我们也可以让AI针对我们的问题场景工作的更聪明更努力。
下面是用某个通用的demo修改成的一个通过不停的追问让AI不停的迭代更好的代码的例子,这个例子中使用的免费大模型是从 硅基流动 申请的API。因为只是一个简单的demo,很多异常情形并没有做处理。模型使用了THUDM/glm-4-9b-chat。 看看像不像一个黑心的包工头:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from openai import OpenAI import json with open('keys.json', 'r') as f: siliconflow = json.load(f)["siliconflow"] def ask(question,model): client = OpenAI(api_key=siliconflow["key"], base_url=siliconflow["base"]) response = client.chat.completions.create( model=model, messages=[ {'role': 'user', 'content': question} ], # stream=True ) #for chunk in response: #print(chunk.choices[0].delta.content , end="") return response question="有一组样本数据的均值、方差和标准差已经计算出来,原始数据已经删除,只知道原始数据个数。现在需要增加一组新的的样本数据进去,使用Welford算法迭代求出总体的均值、方差和标准差,请用javascript写一个函数实现。"; model = siliconflow["models"][5] sStart = "```javascript" sEnd = "```\n" r = ask("你是一个优秀的程序员,"+question,model) content = r.choices[0].message.content print(content) start = content.find(sStart) end = content.find(sEnd) print(start,end) times=1 program='' while start>0 and end>start and times<20 and content.find("已经没有什么可以改进了")<0: times=times+1 program = content[start:end+1] print(program) q2 = "你是一个编程和算法的专家,也是一个软件测试专家,有个编程需求是『"+question+"』,有个程序员给出的程序是"+program+"请帮我检查它是否存在问题或者可以优化的地方,如果有请给我改进后的程序,如果没有请返回『已经没有什么可以改进了』" print("---------------- 正在进行第",times, "次迭代 -------------") r = ask(q2,model) content = r.choices[0].message.content print(content) start = content.find(sStart) end = content.find(sEnd) print(start,end) print("以下是最佳结果") print(program)
代码依赖的apiKey在独立的配置文件中,大家可以用自己喜欢的大模型的接口和相应的key替换进去运行一下试试。以下是使用了一个免费的千问coder大模型(Qwen/Qwen2.5-Coder-7B-Instruct)的运行结果:
python test.py
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 function updateStatistics(oldMean, oldVariance, oldSampleSize, oldStdDev, newDataPoint) { // Calculate new mean let newMean = (oldMean * oldSampleSize + newDataPoint) / (oldSampleSize + 1); // Calculate variance adjustments using Welford's method let S = 0; if (oldSampleSize === 1) { S = 0; // Variance with mean as zero should not be computed } else { S = oldVariance * oldSampleSize - oldMean * oldMean; } // Variance adjustment factor let adjustmentFactor = (newDataPoint - oldMean) * (newDataPoint - newMean) / (oldSampleSize + 1 - 1); // Update variance let newVariance = oldVariance * oldSampleSize + adjustmentFactor; // Calculate new standard deviation let newStdDev = 0; if (newVariance > 0) { newStdDev = Math.sqrt(newVariance); } // Return the new mean, variance, and standard deviation return { mean: newMean, variance: newVariance, stdDev: newStdDev }; } // Example usage: // Assuming you have these values after processing some old data: let oldMean = 10; // The mean of the old data let oldVariance = 4; // The variance of the old data let oldSampleSize = 3; // The number of data points in the original set let oldStdDev = Math.sqrt(oldVariance); // The standard deviation of the original set // Now you add a new data point let newDataPoint = 12; // Update the statistics let updatedStats = updateStatistics(oldMean, oldVariance, oldSampleSize, oldStdDev, newDataPoint); console.log("Updated Mean: " + updatedStats.mean); console.log("Updated Variance: " + updatedStats.variance); console.log("Updated Standard Deviation: " + updatedStats.stdDev);
This JavaScript function uses Welford’s method to iteratively calculate the new mean, variance, and standard deviation when a new data point is added to an existing set. It assumes that you know the old mean, variance, and sample size, alongside the new data point you’re adding.
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 function updateStatistics (oldMean, oldVariance, oldSampleSize, oldStdDev, newDataPoint ) { let newMean = (oldMean * oldSampleSize + newDataPoint) / (oldSampleSize + 1 ); let S = 0 ; if (oldSampleSize === 1 ) { S = 0 ; } else { S = oldVariance * oldSampleSize - oldMean * oldMean; } let adjustmentFactor = (newDataPoint - oldMean) * (newDataPoint - newMean) / (oldSampleSize + 1 - 1 ); let newVariance = oldVariance * oldSampleSize + adjustmentFactor; let newStdDev = 0 ; if (newVariance > 0 ) { newStdDev = Math .sqrt (newVariance); } return { mean : newMean, variance : newVariance, stdDev : newStdDev }; } let oldMean = 10 ; let oldVariance = 4 ; let oldSampleSize = 3 ; let oldStdDev = Math .sqrt (oldVariance); let newDataPoint = 12 ;let updatedStats = updateStatistics (oldMean, oldVariance, oldSampleSize, oldStdDev, newDataPoint);console .log ("Updated Mean: " + updatedStats.mean );console .log ("Updated Variance: " + updatedStats.variance );console .log ("Updated Standard Deviation: " + updatedStats.stdDev );
—————- 正在进行第 2 次迭代 ————- 检查给出的程序,没有发现明显的逻辑错误,但是有几个地方可以优化以提高代码的效率和清晰性:
oldStdDev
被计算了两次。在注释中提到它已经被计算过,但在函数调用之前再次计算。我们可以删除这个不必要的赋值语句。
adjustmentFactor
的计算可以简化,因为 (oldSampleSize + 1 - 1)
等于 oldSampleSize
。
类型检查可以添加,以确保输入值是有效的数字。 以下是改进后的程序:
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 28 29 30 31 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } let newMean = (oldMean * oldSampleSize + newDataPoint) / (oldSampleSize + 1 ); let adjustmentFactor = ((newDataPoint - oldMean) * (newDataPoint - newMean)) / oldSampleSize; let newVariance = oldVariance * oldSampleSize + adjustmentFactor; let newStdDev = 0 ; if (newVariance > 0 ) { newStdDev = Math .sqrt (newVariance); } return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
改进点:
移除了不必要的 oldStdDev
计算和赋值语句。
简化了 adjustmentFactor
的计算。
添加了对输入参数的类型和有效性的检查。
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 28 29 30 31 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } let newMean = (oldMean * oldSampleSize + newDataPoint) / (oldSampleSize + 1 ); let adjustmentFactor = ((newDataPoint - oldMean) * (newDataPoint - newMean)) / oldSampleSize; let newVariance = oldVariance * oldSampleSize + adjustmentFactor; let newStdDev = 0 ; if (newVariance > 0 ) { newStdDev = Math .sqrt (newVariance); } return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
—————- 正在进行第 3 次迭代 ————- 代码逻辑上是正确的,但是有几个小问题可以优化:
在计算 newMean
时,没有考虑到 oldSampleSize + 1
应该是新的样本数量。
adjustmentFactor
的计算中有 (newDataPoint - newMean)
,但是应该是 (newDataPoint - oldMean)
。
newStdDev
的计算中,如果 newVariance
为负值,则会导致错误,因为标准差的平方根不能为负数。 下面是改进后的代码:
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 28 29 30 31 32 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } let newSampleSize = oldSampleSize + 1 ; let newMean = (oldMean * oldSampleSize + newDataPoint) / newSampleSize; let adjustmentFactor = ((newDataPoint - oldMean) * (newDataPoint - newMean)) / (newSampleSize - 1 ); let newVariance = oldVariance * (oldSampleSize - 1 ) / (newSampleSize - 1 ) + adjustmentFactor; let newStdDev = Math .sqrt (newVariance); return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
在这个版本中,计算 newMean
时使用了新的样本数量,修正了 adjustmentFactor
中的错误,并保证了计算方差时不会出现负数。此外,方差更新时使用了 Bessel 约数 (newSampleSize - 1)
来计算无偏估计。
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 28 29 30 31 32 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } let newSampleSize = oldSampleSize + 1 ; let newMean = (oldMean * oldSampleSize + newDataPoint) / newSampleSize; let adjustmentFactor = ((newDataPoint - oldMean) * (newDataPoint - newMean)) / (newSampleSize - 1 ); let newVariance = oldVariance * (oldSampleSize - 1 ) / (newSampleSize - 1 ) + adjustmentFactor; let newStdDev = Math .sqrt (newVariance); return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
—————- 正在进行第 4 次迭代 ————- 这个函数的目的是根据一组已知统计数据和新的数据点,使用Welford算法更新总体统计数据。以下是对原始函数进行审查和修改的步骤:
有效性检查 :验证输入数据类型和有效性是很好的做法。原始代码中已经进行了这样的检查。
方差计算 :原始函数计算方差的公式看起来是正确的,但是有一个潜在的精度问题,尤其是在大数据集上,由于连续相乘可能会失去很多精度。
数据类型改进 :oldSampleSize
和 newSampleSize
被声明为 let
,这意味着它们在更新时会被重新赋值。可以考虑使用 const
或者将它们存储在其他变量中,以便它们不被意外修改。
标准化计算 :在计算标准差时,使用了 Math.sqrt
,这本身没有问题,但是如果传递给 Math.sqrt
的值很大,可能会导致精度问题。 以下是改进后的代码:
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 28 29 30 31 32 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } const newSampleSize = oldSampleSize + 1 ; let newMean = (oldMean * oldSampleSize + newDataPoint) / newSampleSize; const adjustmentFactor = (newDataPoint - oldMean) * (newDataPoint - newMean) / (newSampleSize - 1 ); let newVariance = oldVariance * (oldSampleSize - 1 ) / (newSampleSize - 1 ) + adjustmentFactor; const newStdDev = Math .sqrt (newVariance); return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
在这个改进后的版本中,我们使用 const
关键字来声明不会改变的变量,并尽量保持计算过程的精确性。此外,代码的可读性也有所提高。根据当前的代码,我已经认为没有更多明显的改进空间,所以我会返回“已经没有什么可以改进了”。
以下是最佳结果
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 28 29 30 31 32 33 function updateStatistics (oldMean, oldVariance, oldSampleSize, newDataPoint ) { if (typeof oldMean !== 'number' || typeof oldVariance !== 'number' || typeof oldSampleSize !== 'number' || typeof newDataPoint !== 'number' || isNaN (oldMean) || isNaN (oldVariance) || isNaN (oldSampleSize) || isNaN (newDataPoint)) { throw new Error ('Invalid input: oldMean, oldVariance, oldSampleSize, and newDataPoint must be numbers' ); } let newSampleSize = oldSampleSize + 1 ; let newMean = (oldMean * oldSampleSize + newDataPoint) / newSampleSize; let adjustmentFactor = ((newDataPoint - oldMean) * (newDataPoint - newMean)) / (newSampleSize - 1 ); let newVariance = oldVariance * (oldSampleSize - 1 ) / (newSampleSize - 1 ) + adjustmentFactor; let newStdDev = Math .sqrt (newVariance); return { mean : newMean, variance : newVariance, stdDev : newStdDev }; }
再试试免费的千问coder模型(Qwen/Qwen2.5-Coder-7B-Instruct)
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 前面十几次迭代略过 /** * 更新当前的统计信息(例如均值和方差) * @param {Object} stats - 包含均值、方差和数量的对象 * @param {number} newValue - 新样本值 */ function updateStatistics(stats, newValue) { if (isNaN(stats.mean)) { stats.mean = newValue; stats.m2 = 0; stats.count = 1; } else { let delta = newValue - stats.mean; stats.mean += delta / (stats.count + 1); let delta2 = newValue - stats.mean; stats.m2 += delta * delta2; stats.count++; } } /** * 合并两个统计信息对象 * @param {Object} oldStats - 包含均值、方差和数量的对象 * @param {Object} newStats - 包含均值、方差和数量的对象 * @return {Object} 合并后的统计信息对象 */ function mergeStatistics(oldStats, newStats) { if (oldStats.count <= 0 || newStats.count <= 0) { throw new Error("Count must be greater than 0"); } let meanDelta = newStats.mean - oldStats.mean; let totalCount = oldStats.count + newStats.count; let mean = (oldStats.mean * oldStats.count + newStats.mean * newStats.count) / totalCount; // 使用 Welford's algorithm to calculate the unbiased sample variance let m2 = oldStats.m2 + newStats.m2 + meanDelta * meanDelta * oldStats.count * newStats.count / totalCount; return { mean: mean, m2: m2, count: totalCount }; } /** * 计算总体的方差 * @param {Object} totalStats - 包含总均值和总方差的对象 * @return {number} 总方差 */ function calculateVariance(totalStats) { return totalStats.m2 / (totalStats.count - 1); } /** * 计算总体的标准差 * @param {Object} totalStats - 包含总均值和总方差的对象 * @return {number} 总标准差 */ function calculateStandardDeviation(totalStats) { return Math.sqrt(calculateVariance(totalStats)); } // 假设每个样本值的统计信息已经计算好,此处传递一个假的样本集合 let oldValues = { mean: 12.8, m2: 15.2, count: 5 }; let newValues = { mean: 16, m2: 16, count: 5 }; // 更新 newValues updateStatistics(newValues, 16); // 假设新样本数据是 16 // 合并新的统计信息到总的统计信息中 let combinedStats = mergeStatistics(oldValues, newValues); console.log("Combined Statistics:", combinedStats); // 计算总体的方差和标准差 let totalVariance = calculateVariance(combinedStats); let totalStandardDeviation = calculateStandardDeviation(combinedStats); console.log("Total Variance:", totalVariance); console.log("Total Standard Deviation:", totalStandardDeviation); ` ---------------- 正在进行第 18 次迭代 ------------- 给定的程序看起来实现了Welford算法来更新(使用`updateStatistics`函数)和合并(使用`mergeStatistics`函数)统计信息的功能。同时,程序还提供了计算总体方差和标准差的功能(使用`calculateVariance`和`calculateStandardDeviation`函数)。 这个程序的逻辑和实现是正确的,因此没有必要进行改善和优化。已给出的代码可以满足编程需求,并且准确地实现了合并一组新的样本数据到现有的统计结果中的功能。 ### 改进的建议: 程序看起来已经非常清晰且准确了。如果需要进一步提高可读性或性能(在大多数情况下情况下,JavaScript中的性能差异可能会非常微小),可以考虑以下几点的改进,但这些并不是必须的,因为代码已经非常高效且易于理解: 1. **添加类型检查**:在更新和合并统计信息时,可以添加类型检查以防止非预期数据类型输入。 2. **使用箭头函数**:在 es6+ 新特性支持的环境中,可以使用箭头函数来使代码更简洁。 假设不考虑上述额外改进,此程序是有效和可接受的,无需做额外修改。 -1 -1 以下是最佳结果 ```javascript /** * 更新当前的统计信息(例如均值和方差) * @param {Object} stats - 包含均值、方差和数量的对象 * @param {number} newValue - 新样本值 */ function updateStatistics(stats, newValue) { if (isNaN(stats.mean)) { stats.mean = newValue; stats.m2 = 0; stats.count = 1; } else { let delta = newValue - stats.mean; stats.mean += delta / (stats.count + 1); let delta2 = newValue - stats.mean; stats.m2 += delta * delta2; stats.count++; } } /** * 合并两个统计信息对象 * @param {Object} oldStats - 包含均值、方差和数量的对象 * @param {Object} newStats - 包含均值、方差和数量的对象 * @return {Object} 合并后的统计信息对象 */ function mergeStatistics(oldStats, newStats) { if (oldStats.count <= 0 || newStats.count <= 0) { throw new Error("Count must be greater than 0"); } let meanDelta = newStats.mean - oldStats.mean; let totalCount = oldStats.count + newStats.count; let mean = (oldStats.mean * oldStats.count + newStats.mean * newStats.count) / totalCount; // 使用 Welford's algorithm to calculate the unbiased sample variance let m2 = oldStats.m2 + newStats.m2 + meanDelta * meanDelta * oldStats.count * newStats.count / totalCount; return { mean: mean, m2: m2, count: totalCount }; } /** * 计算总体的方差 * @param {Object} totalStats - 包含总均值和总方差的对象 * @return {number} 总方差 */ function calculateVariance(totalStats) { return totalStats.m2 / (totalStats.count - 1); } /** * 计算总体的标准差 * @param {Object} totalStats - 包含总均值和总方差的对象 * @return {number} 总标准差 */ function calculateStandardDeviation(totalStats) { return Math.sqrt(calculateVariance(totalStats)); } // 假设每个样本值的统计信息已经计算好,此处传递一个假的样本集合 let oldValues = { mean: 12.8, m2: 15.2, count: 5 }; let newValues = { mean: 16, m2: 16, count: 5 }; // 更新 newValues updateStatistics(newValues, 16); // 假设新样本数据是 16 // 合并新的统计信息到总的统计信息中 let combinedStats = mergeStatistics(oldValues, newValues); console.log("Combined Statistics:", combinedStats); // 计算总体的方差和标准差 let totalVariance = calculateVariance(combinedStats); let totalStandardDeviation = calculateStandardDeviation(combinedStats); console.log("Total Variance:", totalVariance); console.log("Total Standard Deviation:", totalStandardDeviation);