为什么我只能访问数组的 39.9%?

How come I can only access 39.9% of my array?

我有一个包含 1000 个数据的数组,但由于某些原因,我只能访问其中 39.9% 的数据。

这个问题出现在我使用名为 jsPsych 的 JavaScript 库编码的反应时间游戏中。在游戏中,用户必须在图像从屏幕上消失之前单击一个按钮。游戏被操纵,以便用户在试验中保持 20% 的成功率。

我使用前高斯数据完成了这个装配。具体来说,我有一个包含 1000 个非高斯数据的数组,在每次试验开始时,我从这些数据的底部 20% 中选择一个数字来表示图像将在用户屏幕上持续多长时间。结果是用户只有 20% 的机会足够快地响应图像。此过程的目标是强制用户在试验中获得 20% 的成功率。

如果用户的持续成功率恰好大于 20%,我将每次试验成功的可能性降低 5%,直到成功率为 0.2。同样,如果用户的持续成功率低于20%,我将尝试成功的可能性提高5%,直到成功率为0.2。

问题是我无法将成功可能性提高到 0.399 以上。它应该能够一直上升到 1.0,在这种情况下,我将从我所有的前高斯数据中采样。但是,如果我允许我的游戏 运行 而不触摸按钮,则成功可能性会以 0.5 的增量增加——这是应该的——直到达到 0.399。然后,它停止增加。

我不知道为什么会这样,或者为什么成功的可能性在应该以 0.5 的增量增加时甚至可以达到 0.399。

您可以在下面查看我的代码。 请注意,您需要检查我登录到控制台的数据,才能清楚地了解我遇到的问题。

// this is just some supporting code
const jsPsych = initJsPsych();
const game = {};

// the ex-Gaussian data, sorted in ascending order
const dist = [];
for (let i = 0; i < 1000; i++) {
  dist.push(jsPsych.randomization.sampleExGaussian(180, 20, 1, positive = true))
}
dist.sort(function(a, b) {
  return a - b
})
console.log("Array of ex-Gaussian data:" + dist)

// initial success likelihood = 20%
let success_likelihood = 0.2;

game.timeline = [{
  timeline: [{
      // wait for image to appear
      type: jsPsychHtmlButtonResponse,
      stimulus: "<div style='visibility:hidden;'>image</div>",
      choices: ["Click me!"],
      trial_duration: 1000,
      response_ends_trial: false,
      // place feedback element here, albeit hidden, to keep page formatting consistent
      prompt: "<div>Click the button as soon as the image appears.</div><div style='visibility:hidden; background-color:green; padding:20px;'>Success</div>",
    },
    {
      // show image
      type: jsPsychHtmlButtonResponse,
      stimulus: "<div>image</div>",
      choices: ["Click me!"],
      trial_duration: function() {
        // player must click button within trial duration to succeed
        // player has x% chance of success on each trial
        // sample trial duration from lower x% of rt distribution
        let cutoff = 1000 * success_likelihood;
        // subtract cutoff - 1 because array index starts at 0
        return dist[cutoff - 1];
      },
      data: {
        type: "game"
      },
      prompt: "<div>Click the button as soon as the image appears.</div><div style='visibility:hidden; background-color:green; padding:20px;'>Success</div>",
    },
    {
      // feedback
      type: jsPsychHtmlButtonResponse,
      stimulus: "<div style='visibility:hidden;'>image</div>",
      choices: ["Click me!"],
      trial_duration: 2000,
      response_ends_trial: false,
      prompt: function() {
        var last_trial = jsPsych.data.getLastTrialData();
        var rt = last_trial.trials[0].rt;
        // if rt === null, player failed to press button within span of trial
        if (rt != null) {
          return "<div>Click the button as soon as the image appears.</div><div style='background-color:green; padding:20px;'>Success</div>";
        } else {
          return "<div>Click the button as soon as the image appears.</div><div style='background-color:red; padding:20px;'>Missed</div>";
        }
      },
    }
  ],
  on_timeline_finish: function() {
    // re-evaluate success rate after each trial 
    let total_trials = jsPsych.data.get().filter({
      type: "game"
    });
    let success_trials = total_trials.select('rt').subset(function(x) {
      return x != null;
    });
    let success_rate = success_trials.count() / total_trials.count();
    console.log("Success rate:" + success_rate)
    // adjust success likelihood +-5% until it reaches 0.2
    if (success_rate > 0.2) {
      success_likelihood -= 0.05;
    } else if (success_rate < 0.2) {
      success_likelihood += 0.05;
    }
    console.log("Success likelihood:" + success_likelihood)
  },
  loop_function: function(data) {
    // stop game once player attains 12 success trials
    let success_trials = jsPsych.data.get().filter({
      type: "game"
    }).select('rt').subset(function(x) {
      return x != null;
    }).count();
    if (success_trials != 12) {
      return true;
    } else {
      return false;
    }
  }
}]

// run code
jsPsych.run([game])
<!DOCTYPE html>
<html lang="en-CA">

<head>
  <meta charset="UTF-8" />
  <title>Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <script src="https://unpkg.com/jspsych@7.1.2/dist/index.browser.min.js"></script>
  <link href="https://unpkg.com/jspsych@7.1.2/css/jspsych.css" rel="stylesheet" />
  <script src="https://unpkg.com/@jspsych/plugin-html-button-response@1.1.0/dist/index.browser.min.js"></script>
  <script src="https://unpkg.com/@jspsych/plugin-html-keyboard-response@1.1.0/dist/index.browser.min.js"></script>
</head>

<body></body>

</html>

有谁知道为什么我的阵列访问受到限制?

success_likelihood根据这个只会变小,不会变大?

if (success_rate > 0.2) {
  success_likelihood -= 0.05;
} else if (success_rate < 0.2) {
  success_likelihood;
}

首先,您根本没有增加 success_likelyhood,但这可能只是一个错字。

你的第二个问题是这段代码

let cutoff = 1000 * success_likelihood;
return dist[cutoff - 1];

由于浮点运算的工作原理,您的 success_likelihood 迟早会是一个小数点后 3 位以上的数字。当发生这种情况时,1000 * success_likelihood 将类似于 399.999999,因此 dist[cutoff -1] 将 return undefined 导致您的代码停止。使用

let cutoff = Math.round(1000 * success_likelihood)

您的 success_likelihood 从 0.2 开始,每次用户成功时减少 0.05。这意味着如果用户连续答对 4 次,您的 success_likelihood 将降为零,导致 trial_duration 被设置为 undefined。浮点舍入错误很常见,因此您的 0.399 可能更像是 0.3999999,或者 10 分之 4。

您可以考虑通过乘以 0.95 而不是减去 0.05,每次将当前剩余 success_likelihood 值减少 5%。