📝

数学で落ちこぼれを「ゼロにする」― 操作から始める、非記述的数学理解ツールの実践 ―

に公開

運動野・視覚野連動による

非記述的数学理解の形成

― Processingを用いた身体化関数学習の実践 ―

式不要。練習問題不要。
変数はマウス、出力は図形。脳が勝手に理解する。

理解しようとするな。操作しろ。
理解は、あとから勝手に追いつく。

数学は暗記じゃない。
入力と出力の関係に、身体が慣れるだけだ。


まずは、完成デモを見てほしい

百聞は一見にしかず。
以下は、今回作成した Processing 製のインタラクティブ数学デモ だ。


この画面では、

  • 円が回転し
  • sin / cos の波が同時に描かれ
  • 右側ではローズ図形が生成され続ける

ということが起きている。

だが――
数式は一切表示されない。


デモの操作方法(超シンプル)

  • Mouse X / Y

    • n, d の値を操作(INTモード時)
  • Click n / d ラベル

    • モード切替
    • INT / PI / SQRT2 / SQRT3 / E
  • SPACE

    • ローズ図形をクリア

操作説明はこれだけだ。
チュートリアルも、練習問題もない。


ソースコード(全文)

※ Processing (Java mode) 用
※ 普通のPCで動作、GPU不要


// =====================================
// VisualizeMath : Rose & Trig Explorer
// =====================================

float theta = 0;        // display theta (0..2PI)
float thetaRose = 0;   // continuous theta for rose
float dTheta = 0.02;

// parameters
float n = 4;
float d = 5;

// mode
String[] modes = { "INT", "PI", "SQRT2", "SQRT3", "E" };
int nMode = 0;
int dMode = 0;

// rose history
ArrayList<PVector> rose = new ArrayList<PVector>();

// layout
float cx = 260;
float cy = 260;
float r  = 100;

// loop detection
float lastTheta = 0;
int loopCount = 0;
boolean flash = false;
int flashTimer = 0;

void setup() {
  size(1000, 620);
  smooth();
}

void draw() {
  background(0);

  updateParameters();

  // advance angles
  theta += dTheta;
  thetaRose += dTheta;

  // detect loop (display theta only)
  if (lastTheta < TWO_PI && theta >= TWO_PI) {
    loopCount++;
    flash = true;
    flashTimer = 10;
    theta -= TWO_PI; // OK: display only
  }
  lastTheta = theta;

  float cosT = cos(theta);
  float sinT = sin(theta);

  drawCircle(cosT, sinT);
  drawCosWave(cosT);
  drawSinWave(sinT);
  drawRose();
  drawUI();
  drawManual();

  updateFlash();
}

// =========================
// Parameter handling
// =========================

void updateParameters() {
  if (modes[nMode].equals("INT")) n = round(map(mouseX, 0, width, 1, 8));
  if (modes[dMode].equals("INT")) d = round(map(mouseY, height-80, 0, 1, 8));

  if (modes[nMode].equals("PI"))    n = PI;
  if (modes[nMode].equals("SQRT2")) n = sqrt(2);
  if (modes[nMode].equals("SQRT3")) n = sqrt(3);
  if (modes[nMode].equals("E"))     n = exp(1);

  if (modes[dMode].equals("PI"))    d = PI;
  if (modes[dMode].equals("SQRT2")) d = sqrt(2);
  if (modes[dMode].equals("SQRT3")) d = sqrt(3);
  if (modes[dMode].equals("E"))     d = exp(1);
}

// =========================
// Circle & radius
// =========================

void drawCircle(float cosT, float sinT) {
  pushMatrix();
  translate(cx, cy);

  stroke(180);
  noFill();
  ellipse(0, 0, r*2, r*2);

  stroke(255, 255, 0);
  strokeWeight(2);
  line(0, 0, cosT*r, sinT*r);

  fill(255);
  text("θ", cosT*r*0.5, sinT*r*0.5);

  popMatrix();
}

// =========================
// Cosine wave (vertical, bottom)
// =========================

void drawCosWave(float cosT) {
  pushMatrix();
  translate(cx, cy + r + 80);

  stroke(255, 140, 0);
  noFill();
  beginShape();
  for (int i = -150; i <= 150; i++) {
    float t = theta + i*0.05;
    vertex(cos(t)*r, i);
  }
  endShape();

  stroke(255);
  line(cosT*r, -150, cosT*r, 150);

  popMatrix();
}

// =========================
// Sine wave (horizontal, right)
// =========================

void drawSinWave(float sinT) {
  pushMatrix();
  translate(cx + r + 80, cy);

  stroke(0, 180, 255);
  noFill();
  beginShape();
  for (int i = -150; i <= 150; i++) {
    float t = theta + i*0.05;
    vertex(i, sin(t)*r);
  }
  endShape();

  stroke(255);
  line(-150, sinT*r, 150, sinT*r);

  popMatrix();
}

// =========================
// Rose curve (FIXED)
// =========================

void drawRose() {
  float px = cx + r + 80 + 300;
  float py = 260;
  float s  = 150;

  float rr = cos((n/d) * thetaRose);
  float rx = rr * cos(thetaRose);
  float ry = rr * sin(thetaRose);

  rose.add(new PVector(rx*s, ry*s));
  if (rose.size() > 8000) rose.remove(0);

  pushMatrix();
  translate(px, py);

  if (flash) {
    stroke(255, 255, 0);
    strokeWeight(2);
  } else {
    stroke(200);
    strokeWeight(1);
  }

  noFill();
  rect(-s, -s, s*2, s*2);

  beginShape();
  for (PVector p : rose) vertex(p.x, p.y);
  endShape();

  fill(255, 0, 0);
  noStroke();
  ellipse(rx*s, ry*s, 5, 5);

  popMatrix();
}

// =========================
// UI
// =========================

void drawUI() {
  fill(255);
  textSize(14);
  text("n = " + modes[nMode] + " (" + nf(n,1,3) + ")", 20, 20);
  text("d = " + modes[dMode] + " (" + nf(d,1,3) + ")", 20, 40);
  text("Loops : " + loopCount, width - 140, height - 20);
}

void drawManual() {
  fill(30);
  noStroke();
  rect(0, height-80, width, 80);

  fill(255);
  textSize(13);
  text(
    "Mouse X / Y : control n, d (INT mode)\n" +
    "Click n / d label : change mode (INT, PI, SQRT2, SQRT3, E)\n" +
    "SPACE : clear rose",
    20, height-55
  );
}

// =========================
// Flash timer
// =========================

void updateFlash() {
  if (flash) {
    flashTimer--;
    if (flashTimer <= 0) flash = false;
  }
}

// =========================
// Input
// =========================

void mousePressed() {
  if (mouseY < 60) {
    if (mouseY < 30) nMode = (nMode + 1) % modes.length;
    else dMode = (dMode + 1) % modes.length;
  }
}

void keyPressed() {
  if (key == ' ') rose.clear();
}


触ってみたあなたへ

ここまでで、多くの人がこう感じるはずだ。

「あ、次にこう動かしたら、
たぶんこういう形になるな」

そして実際に、
ほぼその通りの図形が描かれる。


このデモで起きていることは、極めてシンプルだ

改めて言語化しよう。

このデモで起きていることは、極めてシンプルだ。

  • 入力:マウス操作(n と d)
  • 出力:画面上に描かれる図形
  • 数式の提示:なし

それだけである。

にもかかわらず、人は数回触るだけで

  • 次の変化を予測し
  • 操作と結果を結びつけ
  • 図形の振る舞いを「理解した気」になる

これは錯覚ではない。


脳内で何が起きているのか

このとき脳内では、次の回路が無意識に形成されている。

操作(入力)
   ↓
脳内シミュレーション
   ↓
結果の予測
   ↓
実際の出力

つまり、

入力 → 脳内 → 出力

という関数関係が、
数式を経由せずに成立している。

これは、

  • y = f(x) を解いた
  • 公式を暗記した

とはまったく違う。

むしろ、

y = f(x) という関係そのものが、
脳のネイティブ言語に翻訳された

と言ったほうが正確だ。


数学の「運動神経」という考え方

私はこれを、

数学の運動神経

と呼んでいる。

  • 見て
  • 触って
  • 動かして
  • 予測して
  • 外したら修正する

この一連の流れは、
スポーツや楽器の習得と同じ構造を持つ。

式は、後から付いてくる。


なぜ「一人も落ちこぼれを作らない」のか

従来の数学教育では、

  • 記号を理解できない
  • 式変形についていけない

という理由で、多くの人が脱落する。

だがこの方法では違う。

  • 記号が読めなくてもいい
  • 式が書けなくてもいい

操作できる限り、理解は進む。

子どもでも、大人でも、関係ない。


「誰でも作れる!君だけのAI」へ

このデモは、単なる数学玩具ではない。

実はそのまま、

  • 重み(n)
  • 比率(d)
  • 周期
  • 内挿と外挿
  • 閉じる・閉じない構造

といった AI・機械学習の基礎概念 に直結している。

この考え方をさらに発展させ、
「AIを作りながら理解する」 ために書いたのが、

『誰でも作れる!君だけのAI』

という実践書だ。

https://zenn.dev/practicalspirit/books/29352d9f774823

関連記事

思想的背景と教育的整理については、
以下の記事でより詳しく論じている。

運動野・視覚野連動による非記述的数学理解の形成
― Processingを用いた身体化関数学習の実践報告 ―
https://myon.hatenadiary.com/entry/2026/02/03/195025


おわりに

数学が苦手な人は、才能がないのだろうか。
私は、そうは思わない。

いじる。
慣れる。
想像できる。

これができない人間など、本来一人もいない。

なぜなら、人は 歩ける からだ。

歩くという行為は、

重心を計算し

バランスを崩したら即座に修正し

地面の状態に応じて出力を変える

という、極めて高度なリアルタイム演算の連続だ。

これをプログラムに書き起こしたら、
何百行、何千行になるか分からない。

それでも人は、誰一人として
「歩き方の数式」を知らずに歩ける。

つまり――

人間は、式を知らなくても
入力と出力を結びつけることができる存在だ。

教科書レベルの数学も、本来は同じだ。

慣れない人がいるのではない。
慣れる前に、脱落させているだけなのだ。

人間は本来、

式がなくても、入力と出力を結びつけられる

驚くほど高性能な存在だ。

人間はみんな、f(x) だ。

このデモは、その事実を
ほんの数分で体験させるための装置である。


Discussion