使用SIR模型进行疾病暴发模拟

时间:2018-10-18 05:12:12

标签: c++

我有一个家庭作业,我必须编写一个C ++程序来使用SIR模型(易感,传染性,恢复性)来模拟疾病暴发。要求是使用尺寸为7x7的2D数组,用户可以选择X和Y坐标来初始化感染者。如果附近有感染者,则容易感染者(S)将被感染(I)。然后,如果在附近有恢复者,则被感染者将恢复(R)。如果所有人都康复,该程序将结束。 输出示例:

Day 0                          Day 1                       Day 2
s s s s s s s                  s s s s s s s               s s s s s s s
s s s s s s s                  s s s s s s s               s i i i i i s
s s s s s s s                  s s i i i s s               s i r r r i s
s s s i s s s                  s s i r i s s               s i r r r i s
s s s s s s s                  s s i i i s s               s i r r r i s
s s s s s s s                  s s s s s s s               s i i i i i s
s s s s s s s                  s s s s s s s               s s s s s s s

到目前为止,我只能检查位置(1,1),(1,7),(7,1),(7,7)的状态。如果它旁边的下三个位置有被感染的人,它将状态更新为nextDayState。 到目前为止,这是我的代码,其中包含两个函数SpreadingDisease和RecoverState。

    void recoverState(char currentDayState[SIZE][SIZE], char nextDayState[SIZE][SIZE], int sizeOfArray)//It will take in the currentState of Day 0. I also copy the elements in currentState to nextDayState so that it could work. 
{
    for (int i = 1; i < sizeOfArray + 1; ++i)
    {
        for (int j = 1; j <= sizeOfArray + 1; ++j)
        {
            if (currentDayState[i][j] == 'i')//If found any Infected, update it to Recover on the nextDayState array. 
            {
                nextDayState[i][j] == 'r';
            }
        }
    }

    for (int i = 1; i < sizeOfArray + 1; ++i)
    {
        for (int j = 1; j <= sizeOfArray + 1; ++j)
        {
            currentDayState[i][j] = nextDayState[i][j];
            //After all people are recover, update the currentState and output it to terminal. 
        }
    }
}
void spreadDisease(const char currentDayState[SIZE][SIZE], char nextDayState[SIZE][SIZE], int sizeOfArray, int day = 1)
{
    for (int i = 1; i < sizeOfArray + 1; ++i)
    {
        for (int j = 1; j <= sizeOfArray + 1; ++j)
        {
            if (currentDayState[i][j] == 's')
            {
                if (i == 1 && j == 1)
                {
                    if (currentDayState[1][2] == 'i' || currentDayState[2][1] == 'i' || currentDayState[2][2] == 'i')
                    {
                        nextDayState[1][1] = 'i';
                    }
                }
                if (i == 1 && j == 7)
                {
                    if (currentDayState[1][6] == 'i' || currentDayState[2][6] == 'i' || currentDayState[2][7] == 'i')
                    {
                        nextDayState[1][7] = 'i';
                    }
                }
                if (i == 7 && j == 1)
                {
                    if (currentDayState[6][1] == 'i' || currentDayState[6][2] == 'i' || currentDayState[7][2] == 'i')
                    {
                        nextDayState[7][1] = 'i';
                    }
                }
                if (i == 7 && j == 7)
                {
                    if (currentDayState[6][6] == 'i' || currentDayState[7][6] == 'i' || currentDayState[6][7] == 'i')
                    {
                        nextDayState[7][7] = 'i';
                    }
                }
            }
        }
    }
}

我发现,如果可以某种方式从用户那里获得X和Y坐标,则可以使用该坐标来更新第二天的状态。不幸的是,我不知道如何将X和Y坐标分配给该函数以使其开始。

P / S:感谢您的所有回答。非常感谢您的好意。但是,我之前应该提到我的分配要求。由于我只学习直到“用户定义的函数”部分,因此除此以外,我不允许使用其他任何内容。因此,我仅限于使用2D数组,If-else,循环来解决此问题。现在,xD远远超出了我的知识。

3 个答案:

答案 0 :(得分:1)

这项任务使我想起了我在大学时的日子(那是很久以前的事了)。 好像是Conway's Game of Life的一种变体,当我还是大一学生的时候我就被分配了。因此,我无法抗拒...

之前的一些注释:

  1. 二维数组在C ++中有点不方便。您必须使用恒定大小,或者不使用某种new[](或不符合标准的g ++的VAL扩展名)就无法调整它们的大小。更好的选择通常是std::vector。除了嵌套std::vector外,还可以通过适当的运算符重载来“伪造”这两个维度。令我幸运的是,从我最近的另一个回答Multi-threading benchmarking issues开始,我手头有了一个最小的工作版本。

  2. 关于模拟步骤i,我得出以下逻辑:
    如果患者X是

    • 's':检查她/他周围的所有邻居是否有人被感染('i')。如果是这样,请感染X病人。
    • 'i'(前一天感染):让她/他康复('r')。
    • 'r'(已恢复):对他无所事事,即保持她/他的恢复('r')。

    请注意,可以在电路板上所有行/所有列的一次迭代中完成对当前不同情况的测试-无需在单独的功能中进行此操作。

  3. 最有趣的情况是's'。对于[i] [j]处的患者X,必须检查所有邻居。这些是[i + iP] [j + jP]的患者,iP在[-1,1],jP在[-1,1]。当iP == 0和jP == 0时,对这9个值进行迭代将检查患者X本身。可以检查这种特殊情况,但是我忽略了它(如上述逻辑),患者无法感染自己。这样可以在最内部的循环中节省对iP和jP的额外检查,这是恕我直言的。

  4. 更仔细地了解,如果i == 0或i ==行数-1或j == 0或j,则[i + iP] [j + jP]可能会导致无效坐标==列数-1。这将需要进行大量其他测试才能授予有效索引,但我使用了另一种技巧:使板分别更大以提供边框。我不使用它进行书写,但这为我提供了安全的读取权限。我所要承认的是,从这些边界单元读取数据不会篡改我的仿真逻辑。我用's'初始化包括边界单元的整个电路板。由于从未写入边界单元(初始化中除外),因此也从未感染与我的概念相符的边界单元。

所以,这是我的模拟步骤:

void doSimStep(const Board &board, Board &board1)
{
  assert(board.getNumRows() == board1.getNumRows());
  assert(board.getNumCols() == board1.getNumCols());
  for (size_t i = 1, nRows = board.getNumRows() - 1; i < nRows; ++i) {
    for (size_t j = 1, nCols = board.getNumCols() - 1; j < nCols; ++j) {
      const char person = board[i][j];
      char person1 = person;
      switch (person) {
        case 's': { // search for infection in neighbourhood
          bool infect = false;
          for (int iP = -1; !infect && iP <= 1; ++iP) {
            for (int jP = -1; !infect && jP <= 1; ++jP) {
              infect = board[i + iP][j + jP] == 'i';
            }
          }
          person1 = infect ? 'i' : 's';
        } break;
        case 'i': // infected -> recover
          // fall through
        case 'r': // recovered: stable state
          person1 = 'r';
          break;
        default: assert(false); // Wrong cell contents!
      }
      board1[i][j] = person1;
    }
  }
}

我不明白为什么user10522145认为没有递归就无法做到这一点。 (顺便说一句,我相信相反:每次递归都可以转换成一个迭代,它可以累积或堆叠中间结果。)考虑到OP已经为当前和新的计划了单独的板,我实际上不知道递归的必要位置状态(这大大简化了事情)。

使用9×9板的模拟输出:

Init.:
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s

Day 0:
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s i s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s

Day 1:
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s i i i s s s
 s s s i r i s s s
 s s s i i i s s s
 s s s s s s s s s
 s s s s s s s s s
 s s s s s s s s s

Day 2:
 s s s s s s s s s
 s s s s s s s s s
 s s i i i i i s s
 s s i r r r i s s
 s s i r r r i s s
 s s i r r r i s s
 s s i i i i i s s
 s s s s s s s s s
 s s s s s s s s s

Day 3:
 s s s s s s s s s
 s i i i i i i i s
 s i r r r r r i s
 s i r r r r r i s
 s i r r r r r i s
 s i r r r r r i s
 s i r r r r r i s
 s i i i i i i i s
 s s s s s s s s s

Day 4:
 i i i i i i i i i
 i r r r r r r r i
 i r r r r r r r i
 i r r r r r r r i
 i r r r r r r r i
 i r r r r r r r i
 i r r r r r r r i
 i r r r r r r r i
 i i i i i i i i i

Day 5:
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r
 r r r r r r r r r

No further progress detected on day 6.
Done.

Live Demo on coliru

最后(剧透警报)完整的源代码:

#include <cassert>
#include <iomanip>
#include <iostream>
#include <vector>

template <typename VALUE>
class MatrixT; // forward declaration

template <typename VALUE>
void swap(MatrixT<VALUE>&, MatrixT<VALUE>&); // proto

template <typename VALUE>
class MatrixT {
  friend void swap<VALUE>(MatrixT<VALUE>&, MatrixT<VALUE>&);
  public:
    typedef VALUE Value;
  private:
    size_t _nRows, _nCols;
    std::vector<Value> _values;
  public:
    MatrixT(size_t nRows, size_t nCols, Value value = (Value)0):
      _nRows(nRows), _nCols(nCols), _values(_nRows * _nCols, value)
    { }
    ~MatrixT() = default;
    MatrixT(const MatrixT&) = default;
    MatrixT& operator=(const MatrixT&) = default;

    size_t getNumCols() const { return _nCols; }
    size_t getNumRows() const { return _nRows; }
    const std::vector<Value>& get() const { return _values; }
    Value* operator[](size_t i) { return &_values[0] + i * _nCols; }
    const Value* operator[](size_t i) const { return &_values[0] + i * _nCols; }
};

template <typename VALUE>
void swap(MatrixT<VALUE> &mat1, MatrixT<VALUE> &mat2)
{
  std::swap(mat1._nRows, mat2._nRows);
  std::swap(mat1._nCols, mat2._nCols);
  std::swap(mat1._values, mat2._values);
}

typedef MatrixT<char> Board;

bool operator==(const Board &board1, const Board &board2)
{
  return board1.getNumRows() == board2.getNumRows()
    && board1.getNumCols() == board2.getNumCols()
    && board1.get() == board2.get();
}

std::ostream& operator<<(std::ostream &out, const Board &board)
{
  for (size_t i = 1, nRows = board.getNumRows() - 1; i < nRows; ++i) {
    for (size_t j = 1, nCols = board.getNumCols() - 1; j < nCols; ++j) {
      out << ' ' << board[i][j];
    }
    out << '\n';
  }
  return out;
}

void doSimStep(const Board &board, Board &board1)
{
  assert(board.getNumRows() == board1.getNumRows());
  assert(board.getNumCols() == board1.getNumCols());
  for (size_t i = 1, nRows = board.getNumRows() - 1; i < nRows; ++i) {
    for (size_t j = 1, nCols = board.getNumCols() - 1; j < nCols; ++j) {
      const char person = board[i][j];
      char person1 = person;
      switch (person) {
        case 's': { // search for infection in neighbourhood
          bool infect = false;
          for (int iP = -1; !infect && iP <= 1; ++iP) {
            for (int jP = -1; !infect && jP <= 1; ++jP) {
              infect = board[i + iP][j + jP] == 'i';
            }
          }
          person1 = infect ? 'i' : 's';
        } break;
        case 'i': // infected -> recover
          // fall through
        case 'r': // recovered: stable state
          person1 = 'r';
          break;
        default: assert(false); // Wrong cell contents!
      }
      board1[i][j] = person1;
    }
  }
}

int main()
{
  size_t nRows = 9, nCols = 9;
#if 0 // disabled for demo
  std::cout << "N Rows: "; std::cin >> nRows;
  std::cout << "N Cols: "; std::cin >> nCols;
  /// @todo check nRows, nCols for sufficient values
#endif // 0
  // init board
  std::cout << "Init.:\n";
  Board board(nRows + 2, nCols + 2);
  std::fill(board[0], board[nRows + 2], 's');
  std::cout << board << '\n';
  // infect somebody
  size_t i = nRows / 2 + 1, j = nCols / 2 + 1;
#if 0 // disabled for demo
  std::cout << "Patient 0:\n";
  std::cout << "row: "; std::cin >> i;
  std::cout << "col: "; std::cin >> j;
  /// @todo check i, j for matching the boundaries
#endif // 0
  board[i][j] = 'i';
  // simulation loop
  for (unsigned day = 0;;) {
    std::cout << "Day " << day << ":\n";
    std::cout << board << '\n';
    // simulate next day
    ++day;
    Board board1(board);
    doSimStep(board, board1);
    if (board == board1) {
      std::cout << "No further progress detected on day "
        << day << ".\n";
      break; // exit sim. loop
    }
    // store data of new day
    swap(board, board1);
  }
  // done
  std::cout << "Done.\n";
  return 0;
}

答案 1 :(得分:1)

您正在使用 C ++ ,因此请尽量使用标准库...

神奇优化的疾病模拟功能:

/*
 *-----------------------
 * Key:
 * ----------------------
 * 0 - Susceptible person
 * 1 - Infected person
 * 2 - Recovered person
 * 
 * @param init_infect_x Person to infect at x position...
 * @param init_infect_y Person to infect at y position...
 * @param map_size_x Width of the map...
 * @param map_size_y Height of the map...
 */
std::vector<std::vector<std::vector<int>>> disease_simulator(size_t const init_infect_x = 0u,
                                                             size_t const init_infect_y = 0u,
                                                             size_t const map_size_x = 7u, size_t const map_size_y = 7u)
{
    if (map_size_x == 0u || map_size_y == 0u || init_infect_x + 1 > map_size_x || init_infect_x + 1 < 0 || init_infect_y
        + 1 > map_size_y || init_infect_y + 1 < 0) // Well, we can't create a map which is empty...
        return std::vector<std::vector<std::vector<int>>>();
    std::vector<std::vector<std::vector<int>>> map_list;
    std::vector<std::pair<int, int>> spread_pos;
    std::vector<std::vector<int>> map(map_size_y, std::vector<int>(map_size_x, 0));
    map[init_infect_y][init_infect_x] = 1;
    map_list.emplace_back(map);
    while (std::adjacent_find(map.begin(), map.end(), std::not_equal_to<>()) != map.end())
    {
        for (auto i = 0; i < signed(map.size()); i++)
            for (auto j = 0; j < signed(map[i].size()); j++)
                if (map[i][j] == 1)
                {
                    map[i][j] = 2;
                    spread_pos.emplace_back(std::make_pair(j, i));
                }
        for (auto const pos : spread_pos)
        {
            if (pos.second - 1 >= 0 && map[pos.second - 1][pos.first] == 0) // Up...
                map[pos.second - 1][pos.first] = 1;
            if (pos.first - 1 >= 0 && map[pos.second][pos.first - 1] == 0) // Left...
                map[pos.second][pos.first - 1] = 1;
            if (pos.second - 1 >= 0 && pos.first - 1 >= 0 && map[pos.second - 1][pos.first - 1] == 0) // Up left...
                map[pos.second - 1][pos.first - 1] = 1;
            if (pos.second - 1 >= 0 && pos.first + 2 <= signed(map_size_x) && map[pos.second - 1][pos.first + 1] == 0)
                // Up right...
                map[pos.second - 1][pos.first + 1] = 1;
            if (pos.second + 2 <= signed(map_size_y) && map[pos.second + 1][pos.first] == 0) // Down...
                map[pos.second + 1][pos.first] = 1;
            if (pos.first + 2 <= signed(map_size_x) && map[pos.second][pos.first + 1] == 0) // Right...
                map[pos.second][pos.first + 1] = 1;
            if (pos.second + 2 <= signed(map_size_y) && pos.first + 2 <= signed(map_size_x) && map[pos.second + 1][pos.
                first + 1] == 0) // Down right...
                map[pos.second + 1][pos.first + 1] = 1;
            if (pos.second + 2 <= signed(map_size_y) && pos.first - 1 >= 0 && map[pos.second + 1][pos.first - 1] == 0)
                // Down left...
                map[pos.second + 1][pos.first - 1] = 1;
        }
        map_list.emplace_back(map);
        spread_pos.clear();
    }
    return map_list;
}

此功能的作用是它可以同时为您提供每天的地图,现在您可以一个一个地遍历它们。

  

注意: 此外,不要忘记在std::adjacent_find()的开头#include <algorithm> ...

示例:

int main()
{
    auto days_map = disease_simulator();
    for (auto i = 0u; i < days_map.size(); i++)
    {
        std::cout << "Day " << i << ":" << std::endl;
        for (auto elem2 : days_map[i])
        {
            for (auto elem3 : elem2)
                switch (elem3)
                {
                case 0:
                    std::cout << "s ";
                    break;
                case 1:
                    std::cout << "i ";
                    break;
                case 2:
                    std::cout << "r ";
                    break;
                default:
                    std::cout << ' ';
                    break;
                }
            std::cout << std::endl;
        }
        std::cout << std::endl;
    }
    std::cout << "All people have recovered!" << std::endl;
    return 0;
}

编辑: Live on coliru (使用以中心为感染点的9x9阵列)

好吧,看看它是否能提供您想要的输出...

亲切的问候,

Ruks。

答案 2 :(得分:0)

我猜想在这种情况下迭代可能无法正常工作,因为建议您使用带有数组边界值的递归作为停止递归的条件。 希望有道理