【问题标题】:How to infer state of unknown variables given that you know something changed?鉴于您知道某些事情发生了变化,如何推断未知变量的状态?
【发布时间】:2015-08-27 01:04:43
【问题描述】:

存在一个游戏,其状态由n 布尔变量p_1p_2、...、p_n 量化。假设0 <= n <= 10。游戏的目的是尽可能多地跟踪变量的状态,以及它们何时会改变状态(见下文)。

一次只能观察到变量的一个子集。当变量的状态从 true 更改为 false 时,我们总是会收到通知,但只有在观察相关变量时,我们才会知道哪个变量发生了更改。

如果某些p_it 时将状态从true 更改为false,那么它将在t + 60 时将状态从false 更改回true

以下是一些示例情况和所需的行为。假设有p_1p_2p_3三个变量。

  • 所有变量都是可观察的。一个变量改变了状态,通过观察它是p_1。我们知道p_1 将状态更改回t + 60
  • p_1p_2 是可观察到的。变量改变状态。应该推断是p_3。我们知道p_3 将状态更改回t + 60
  • p_1 是可观察的,并且已知 p_2 在时间 t - 30 时将状态更改为 false。变量改变状态。通过观察不是p_1p_2 只会在t + 30 处将状态更改为true,因此它是p_3
  • 没有可观察到的变量 已知p_1 在时间t - 30 将状态更改为falsep_2t - 75 将状态更改为false,并且p_3 在@更改状态为false 987654362@。变量改变状态。在t + 1 观察到p_2 的值为true。因为p_1 仍然是false,而p_2 已经被观察到是true,所以应该推断p_3false,并且会在t + 60 处将状态改回true
  • p_1p_2 是可观察到的。已知p_3 在时间t - 70 时将状态更改为false。没有变量改变状态。应该推断p_3 为真,因为它会在t - 10 处从false 更改为true,如果它从true 更改为false,我们将被告知一个变量改变状态。

我尝试过的一种方法是在每次通知变量状态更改时迭代每个变量,并根据以下条件排除可能的匹配项:

  • 是否可见且未更改状态
  • 它的最后一次状态变化是否已知,并且在过去还不够远,无法恢复。

然后,对于状态已知的剩余变量,根据变量在特定状态下最后一次观察到的时间,当变量可能已经改变状态(返回,从falsetrue) 等。那么如果在该范围内仅存在一个变量更改事件,则应该是与该变量对应的事件。结果太麻烦了,我没有继续。每一行都是一个负担,因为看起来可能的状态组合的数量太多了,我不会考虑一些事情。

考虑到问题陈述,上述方法是否合理?有没有一种通用的方法可以对此类问题进行建模以更优雅地解决它们?

【问题讨论】:

  • 我建议将该过程建模为一组假设。对于每个通知,创建n 假设哪个变量已更改并将它们与现有变量组合。检查假设的有效性应该是直截了当的(对于每个假设,跟踪您知道变量状态的间隔)。如果您发现无效的,只需将其删除。这可能会以指数方式产生许多有效假设(但这就是结果)。如果您只想要一个解决方案,那么深度优先遍历假设空间可能是一个好主意(基本上是回溯)。
  • 还可以按时间对通知进行排序。
  • 我认为这可行。我在想象一棵树,其中i + 1 级别的节点对应于i 通知之后的可能状态。边缘是假设。这个想法是通知“在它们进来时”被处理,所有这些都对当前状态进行评分很重要,所以我认为只需要最低级别。根据新的假设或观察,可以修剪矛盾的叶子,并从剩余的叶子中生成后续状态。可能状态数量的最坏情况不是n^n吗?
  • 是的,最坏情况的复杂度是 n_variables^n_notifications。但在最坏的情况下,这就是有效解决方案的数量。所以你不能变得更好。除非您只想要一个有效的解决方案。然后,不需要跟踪所有有效的部分解决方案。
  • 就我而言,我有兴趣将各州合并在一起,看看他们都同意什么。我发布了我的解决方案作为受您建议启发的答案。感谢您的帮助!

标签: algorithm language-agnostic logic inference


【解决方案1】:

根据@NicoSchertler 的建议,我提出了一个解决方案,它通过基于ObservationsHypotheses 的序列创建状态集来处理不确定性。观察是特定(观察到的)变量的已知状态,而假设是状态的通知,但没有关于它适用于哪个变量的信息。我们能够做出假设不能应用于正在观察的变量。此解决方案不处理启动状态未知(尚)的情况。

有一个起始状态对应于每个变量都是true 的情况。当提供hypothesis 时,假设每个未观察到的变量都是hypothesis 的主题,可能会生成多个(n)后继状态。导致矛盾的后继状态被丢弃。当提供observation 时,将为每个当前状态生成一个后继状态。任何导致矛盾的状态都被丢弃。通过这种方式,假设和观察的序列导致变量可能处于的一组可能状态。

出于我的特定目的,我想知道根据这些状态可以知道什么(而不是例如是否存在有效的解决方案)。如果变量在所有状态中具有相同的状态,则将状态组合并返回一个结果,该结果给出每个变量的状态。

鉴于n 状态和m 通知,最坏情况的复杂性是n^m,这可能非常有限,但对于我有限的应用程序来说应该没问题。

这里是 JavaScript 实现和测试代码。

solver.js

// Time for state to change back.
var STATE_CHANGE = 6e4;
// Possible notification lag.
var EPSILON = 2e3;

// Comparison operations.
function lt(a, b) {
  return a - b < EPSILON;
}

function gt(a, b) {
  return b - a < EPSILON;
}

function eq(a, b) {
  return Math.abs(a - b) < EPSILON;
}

// Object clone.
function clone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

module.exports = Solver;

/**
 * Solver solves boolean dynamic state.
 * @param {Array<string>} variables - array of variable names.
 */
function Solver(variables) {
  this.variables = {};
  this.states = [];
  this._time = null;
  var state = {};
  var time = Date.now();
  var self = this;
  // TODO: Handle unknown or variable start.
  variables.forEach(function (variable) {
    self.variables[variable] = {
      observed: false
    };
    state[variable] = {
      state: true,
      intervals: [{
        state: true,
        start: time,
        observed: false,
        end: null
      }]
    };
  });
  this.states.push(state);
}

// Set subset of variables as observed, the rest assumed not.
Solver.prototype.setObserved = function(variables) {
  var unobserved_variables = Object.keys(this.variables).filter(function (variable) {
    return variables.indexOf(variable) === -1;
  });
  var self = this;
  variables.forEach(function (variable) {
    self.variables[variable].observed = true;
  });
  unobserved_variables.forEach(function (variable) {
    self.variables[variable].observed = false;
  });
};

// Hypothesis has time, state.
Solver.prototype.addHypothesis = function(h) {
  this.updateVariables();
  var states = [];
  for (var i = 0; i < this.states.length; i++) {
    var newStates = this.applyHypothesis(this.states[i], h);
    if (newStates)
      Array.prototype.push.apply(states, newStates);
  }
  this.states = states;
};

// Observation has time, state, variable.
Solver.prototype.addObservation = function(o) {
  this.updateVariables();
  var states = [];
  for (var i = 0; i < this.states.length; i++) {
    var newState = this.applyObservation(this.states[i], o);
    if (newState)
      states.push(newState);
  }
  this.states = states;
};

// Get set of possible states.
Solver.prototype.getStates = function() {
  this.updateVariables();
  return this.states.slice();
};

// Get consolidated state.
// Each variable has state (true|false|null), change (if false). change
// is number or array (if there is disagreement)
Solver.prototype.getState = function() {
  this.updateVariables();
  // Construct output.
  var out = {};
  var state = this.states[0];
  for (var name in state) {
    var variable = state[name];
    if (variable.state) {
      out[name] = {
        state: variable.state
      };
    } else {
      var time = variable.intervals[variable.intervals.length - 1].end;
      out[name] = {
        state: variable.state,
        time: time
      };
    }
  }
  // Compare results across all states.
  return this.states.slice(1).reduce(function (out, state) {
    for (var name in out) {
      var out_variable = out[name],
          variable = state[name];
      // Check for matching states.
      if (out_variable.state === variable.state) {
        // Falsy check time.
        if (!out_variable.state) {
          // TODO: check undefined in case interval not updated?
          var change = variable.intervals[variable.intervals.length - 1].end;
          if (out_variable.time instanceof Array) {
            if (out_variable.time.indexOf(change) === -1) {
              out_variable.push(change);
            }
          } else if (out_variable.time !== change) {
            var times = [out_variable.time, change];
            out_variable.time = times;
          } // Else matches, so no problem.
        }
      } else {
        // Conflicted states.
        out_variable.state = null;
        // In case it was set.
        delete out_variable.time;
      }
    }
    return out;
  }, out);
};

// Update `false` state variables based on false end
// time, if present.
Solver.prototype.updateVariables = function() {
  var time = this._time || Date.now();
  for (var i = 0; i < this.states.length; i++) {
    var state = this.states[i];
    for (var name in state) {
      var variable = state[name];
      // Update changeback.
      if (!variable.state) {
        if (variable.intervals.length > 0) {
          var last = variable.intervals[variable.intervals.length - 1];
          if (last.end && last.end <= time) {
            // update to true.
            variable.state = true;
            variable.intervals.push({
              state: true,
              start: time,
              end: null
            });
          }
        }
      }
    }
  }
};

// Return state with observation applied or null if invalid.
Solver.prototype.applyObservation = function(state, observation) {
  var variable = state[observation.variable];
  if (variable.state && !observation.state) {
    // Change in observed variable true -> false
    variable.state = observation.state;
    variable.intervals.push({
      state: variable.state,
      start: observation.time,
      end: observation.time + STATE_CHANGE
    });
    return state;
  } else if (variable.state && observation.state) {
    // Expected state.
    return state;
  } else if (!variable.state && observation.state) {
    // Potentially updating variable.
    var time = variable.intervals[variable.intervals.length - 1];
    if (eq(time, observation.time)) {
      // update state.
      variable.state = observation.state;
      variable.intervals.push({
        state: observation.state,
        start: observation.time,
        end: null
      });
      return state;
    } else {
      // Could not update this variable.
      return null;
    }
  } else if (!variable.state && !observation.state) {
    // Expected state.
    return state;
  }
};

// Returns multiple states or null if invalid
Solver.prototype.applyHypothesis = function(state, hypothesis) {
  hypothesis = clone(hypothesis);
  var states = [];
  for (var name in state) {
    // Skip observed variables, no guessing with them.
    if (this.variables[name].observed)
      continue;
    var newState = clone(state);
    var variable = newState[name];
    // Hypothesis is always false.
    if (variable.state) {
      // Change in observed variable true -> false
      variable.state = hypothesis.state;
      variable.intervals.push({
        state: variable.state,
        start: hypothesis.time,
        end: hypothesis.time + STATE_CHANGE
      });
    } else {
      newState = null;
    }
    if (newState !== null) {
      states.push(newState);
    }
  }
  if (states.length === 0) {
    return null;
  } else {
    return states;
  }
};

test-solver.js

var Solver = require('./solver');

var p_1 = "p_1",
    p_2 = "p_2",
    p_3 = "p_3";
var solver = new Solver([p_1, p_2, p_3]);

var t = Date.now();

solver.setObserved([p_1, p_2, p_3]);
solver._time = t + 5e3;
solver.addObservation({
  variable: p_1,
  state: false,
  time: t + 5e3
});

var result = solver.getState();
if (!result[p_1].state && result[p_1].time === t + 65e3 &&
    result[p_2].state &&
    result[p_3].state) {
  console.log("PASS: Test 1.");
} else {
  console.log("FAIL: Test 1.");
}

solver = new Solver([p_1, p_2, p_3]);
solver.setObserved([p_1, p_2]);
solver._time = t + 5e3;
solver.addHypothesis({
  state: false,
  time: t + 5e3
});

result = solver.getState();
if (result[p_1].state &&
    result[p_2].state &&
    !result[p_3].state && result[p_3].time === t + 65e3) {
  console.log("PASS: Test 2.");
} else {
  console.log("FAIL: Test 2.");
}

solver = new Solver([p_1, p_2, p_3]);
solver.setObserved([p_1]);
solver._time = t - 30e3;
solver.addObservation({
  variable: p_2,
  time: t - 30e3,
  state: false
});
solver._time = t;
solver.addHypothesis({
  state: false,
  time: t
});

var result = solver.getState();
if (result[p_1].state &&
    !result[p_2].state && result[p_2].time === t + 30e3 &&
    !result[p_3].state && result[p_3].time === t + 60e3) {
  console.log("PASS: Test 3.");
} else {
  console.log("FAIL: Test 3.");
}

solver = new Solver([p_1, p_2, p_3]);
solver._time = t - 80e3;
solver.addObservation({
  variable: p_3,
  time: t - 80e3,
  state: false
});
solver._time = t - 75e3;
solver.addObservation({
  variable: p_2,
  time: t - 75e3,
  state: false
});
solver._time = t - 30e3;
solver.addObservation({
  variable: p_1,
  time: t - 30e3,
  state: false
});
solver._time = t;
solver.addHypothesis({
  state: false,
  time: t
});
result = solver.getState();
if (!result[p_1].state && result[p_1].time === t + 30e3 &&
    result[p_2].state === null &&
    result[p_3].state === null) {
  console.log("PASS: Test 4.");
} else {
  console.log("FAIL: Test 4.");
}
solver._time = t + 1;
solver.addObservation({
  variable: p_2,
  time: t + 1,
  state: true
});
var result = solver.getState();
if (!result[p_1].state && result[p_1].time === t + 30e3 &&
    result[p_2].state &&
    !result[p_3].state && result[p_3].time === t + 60e3) {
  console.log("PASS: Test 5.");
} else {
  console.log("FAIL: Test 5.");
}

【讨论】:

    猜你喜欢
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多