Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
@@ -1,66 +1,66 @@
# 基本データ型
コンピュータ内のデータについて考える、テキスト、画像、動画、音声、3Dモデルなど、様々な形式が思い浮かます。これらの組織的な形式は異なりますが、すべて様々な基本データ型から構成されています。
コンピュータ内のデータについて考えるとき、テキスト、画像、動画、音声、3D モデルなど、さまざまな形態を思い浮かます。これらのデータの構成形式はそれぞれ異なりますが、いずれも各種の基本データ型によって成り立っています。
**基本データ型とは、CPUが直接操作できるもの**であり、アルゴリズムで直接使用されます。主に以下が含まれます。
**基本データ型CPU が直接演算できる**であり、アルゴリズムの中で直接使れます。主なものは次のとおりです。
- 整数型`byte``short``int``long`
- 浮動小数点型:`float``double`小数を表現するために使
- 文字型`char`、様々な言語の文字、句読点、さらには絵文字を表現するために使用
- ブール型:`bool`、「はい」または「いいえ」の判を表現するために使
- 整数型 `byte``short``int``long`
- 浮動小数点数型 `float``double` 小数を表ために使います。
- 文字型 `char` ,各言語の文字、句読点、さらには絵文字などを表すために使います。
- 真偽値型 `bool` ,真か偽かの判を表ために使います。
**基本データ型はコンピュータ内で二進形式で格納されます**。1つの二進桁は1ビットです。ほとんどの現代的なオペレーティングシステムでは、1バイトは8ビットで構成されています。
**基本データ型はコンピュータ内で 2 進数の形で格納されます**。1 つの二進桁は $1$ ビットです。現代のほとんどのオペレーティングシステムでは、$1$ バイト(byte)は $8$ ビット(bitで構成されます。
基本データ型の値の範囲は、それらが占める空間のサイズに依存します。以下ではJavaを例に説明します。
基本データ型の値は、その型が占める領域の大きさによって決まります。以下では Java を例に取ります。
- 整数型`byte`は1バイト = 8ビットを占め、$2^8$個の数を表現できます。
- 整数型`int`は4バイト = 32ビットを占め、$2^{32}$個の数を表現できます。
- 整数型 `byte` は $1$ バイト = $8$ ビットを占め、$2^{8}$ 個の数を表ます。
- 整数型 `int` は $4$ バイト = $32$ ビットを占め、$2^{32}$ 個の数を表ます。
以下の表は、Javaにおける様々な基本データ型が占める空間、値の範囲、デフォルト値を示しています。この表を暗記する必要はありませんが、一般的な理解を持ち、必要時に参照することをお勧めします
表は、Java における各種基本データ型の使用領域、値域、デフォルト値を示したものです。この表を暗記する必要はなく、大まかに理解しておけば十分であり、必要になったときに参照すればかまいません
<p align="center"> 表 <id> &nbsp; 基本データ型が占める空間と値の範囲 </p>
<p align="center"> 表 <id> &nbsp; 基本データ型の使用領域と値域 </p>
| 型 | シンボル | 占有空間 | 最小値 | 最大値 | デフォルト値 |
| ------- | -------- | -------- | ------------------------ | ----------------------- | -------------- |
| 整数 | `byte` | 1バイト | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | 0 |
| | `short` | 2バイト | $-2^{15}$ | $2^{15} - 1$ | 0 |
| | `int` | 4バイト | $-2^{31}$ | $2^{31} - 1$ | 0 |
| | `long` | 8バイト | $-2^{63}$ | $2^{63} - 1$ | 0 |
| 浮動小数点 | `float` | 4バイト | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ |
| | `double` | 8バイト | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | 0.0 |
| 文字 | `char` | 2バイト | 0 | $2^{16} - 1$ | 0 |
| ブール | `bool` | 1バイト | $\text{false}$ | $\text{true}$ | $\text{false}$ |
| 型 | 記号 | 使用領域 | 最小値 | 最大値 | デフォルト値 |
| ------ | -------- | -------- | ------------------------ | ----------------------- | -------------- |
| 整数 | `byte` | 1 バイト | $-2^7$ ($-128$) | $2^7 - 1$ ($127$) | $0$ |
| | `short` | 2 バイト | $-2^{15}$ | $2^{15} - 1$ | $0$ |
| | `int` | 4 バイト | $-2^{31}$ | $2^{31} - 1$ | $0$ |
| | `long` | 8 バイト | $-2^{63}$ | $2^{63} - 1$ | $0$ |
| 浮動小数点 | `float` | 4 バイト | $1.175 \times 10^{-38}$ | $3.403 \times 10^{38}$ | $0.0\text{f}$ |
| | `double` | 8 バイト | $2.225 \times 10^{-308}$ | $1.798 \times 10^{308}$ | $0.0$ |
| 文字 | `char` | 2 バイト | $0$ | $2^{16} - 1$ | $0$ |
| 真偽値 | `bool` | 1 バイト | $\text{false}$ | $\text{true}$ | $\text{false}$ |
記の表はJavaの基本データ型に特有であることにご注意ください。すべてのプログラミング言語には独自のデータ型定義があり、占有空間、値の範囲、デフォルト値異なる場合があります。
注意してください。上表は Java の基本データ型に対するものです。各プログラミング言語にはそれぞれ独自のデータ型定義があり、使用領域、値域、デフォルト値異なる場合があります。
- Pythonでは、整数型`int`は任意のサイズになることができ、利用可能なメモリによってのみ制限されます。浮動小数点`float`は倍精度64ビットです。`char`型は存在せず、単一文字は実際には長さ1の文字列`str`です。
- CおよびC++では基本データ型のサイズが指定されておらず、実装プラットフォームによって異なります。上記の表はLP64[データモデル](https://en.cppreference.com/w/cpp/language/types#Properties)に従っており、LinuxmacOSを含むUnix 64ビットオペレーティングシステムで使用されています。
- CおよびC++における`char`のサイズは1バイトですが、ほとんどのプログラミング言語では、特定の文字エンコーディング方法に依存し、詳細は「文字エンコーディング」の章で説明されています
- ブール値の表現には1ビット(0または1)のみが必要ですが、通常はメモリ内に1バイトとして格納されます。これは、現代のコンピュータCPUが通常1バイトを最小のアドレス可能なメモリ単位として使用するためです。
- Python では、整数型 `int` は利用可能なメモリに制限されるだけで任意の大きさを取れます。浮動小数点`float` は倍精度 64 ビットです。`char` 型はなく、1 文字は実際には長さ 1 の文字列 `str` です。
- CC++ では基本データ型の大きさは明確に規定されておらず、実装プラットフォームによって異なります。上表は LP64 [データモデル](https://en.cppreference.com/w/cpp/language/types#Properties) に従っており、LinuxmacOS を含む Unix 64 ビット OS で用いられています。
- `char` の大きさは C と C++ では 1 バイトですが、多くのプログラミング言語では採用する文字エンコーディング方式によって決まります。詳しくは「文字エンコーディング」の章を参照してください
- 真偽値を表すのに必要なのは 1 ビット($0$ または $1$)だけですが、メモリ上では通常 1 バイトとして格納されます。これは、現代のコンピュータ CPU が通常 1 バイトを最小のアドレス指定可能なメモリ単位としてるためです。
では、基本データ型とデータ構造の関係は何でしょうかデータ構造とは、コンピュータ内でデータを組織し格納する方法であることを知っています。ここでの焦点は「データ」ではなく「構造」です。
では、基本データ型とデータ構造の間にはどのような関係があるのでしょうかデータ構造とは、コンピュータ内でデータを組織し格納する方法のことです。この言葉で主役なのは「データ」ではなく「構造」です。
「数値の列」を表したい場合、自然に配列の使用を考えます。これは、配列の線形構造が数値の隣接性と順序性を表現できるためですが、格納される内容が整数`int`、小数`float`、文字`char`のいずれであっても、「データ構造」とは関係です
「数字の並び」を表したいなら、自然に配列の使用を思い浮かべるでしょう。これは、配列の線形構造が数字どうしの隣接関係や順序関係を表せるからです。しかし、格納る内容が整数 `int` なのか、小数 `float` なのか、文字 `char` なのかは、「データ構造」とは関係ありません
言い換えると、**基本データ型はデータの「内容型」を提供し、データ構造はデータの「組織方法」を提供します**。例えば、以下のコードでは、同じデータ構造(配列)を使用して、`int``float``char``bool`など異なる基本データ型を格納表現しています。
言い換えると、**基本データ型はデータの「内容型」を提供し、データ構造はデータの「組織方法」を提供します**。たとえば次のコードでは、同じデータ構造(配列)を使って `int``float``char``bool` など異なる基本データ型を格納表現しています。
=== "Python"
```python title=""
# 様々な基本データ型を使用して配列を初期化
# さまざまな基本データ型配列を初期化する
numbers: list[int] = [0] * 5
decimals: list[float] = [0.0] * 5
# Pythonの文字は実際には長さ1の文字列
# Python の文字は実際には長さ 1 の文字列
characters: list[str] = ['0'] * 5
bools: list[bool] = [False] * 5
# Pythonのリストは様々な基本データ型とオブジェクト参照を自由に格納可能
# Python のリストはさまざまな基本データ型とオブジェクト参照を自由に格納できる
data = [0, 0.0, 'a', False, ListNode(0)]
```
=== "C++"
```cpp title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
int numbers[5];
float decimals[5];
char characters[5];
@@ -70,7 +70,7 @@
=== "Java"
```java title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
int[] numbers = new int[5];
float[] decimals = new float[5];
char[] characters = new char[5];
@@ -80,7 +80,7 @@
=== "C#"
```csharp title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
int[] numbers = new int[5];
float[] decimals = new float[5];
char[] characters = new char[5];
@@ -90,7 +90,7 @@
=== "Go"
```go title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
var numbers = [5]int{}
var decimals = [5]float64{}
var characters = [5]byte{}
@@ -100,7 +100,7 @@
=== "Swift"
```swift title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
let numbers = Array(repeating: 0, count: 5)
let decimals = Array(repeating: 0.0, count: 5)
let characters: [Character] = Array(repeating: "a", count: 5)
@@ -110,14 +110,14 @@
=== "JS"
```javascript title=""
// JavaScriptの配列は様々な基本データ型とオブジェクトを自由に格納可能
// JavaScript の配列はさまざまな基本データ型とオブジェクトを自由に格納できる
const array = [0, 0.0, 'a', false];
```
=== "TS"
```typescript title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
const numbers: number[] = [];
const characters: string[] = [];
const bools: boolean[] = [];
@@ -126,7 +126,7 @@
=== "Dart"
```dart title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
List<int> numbers = List.filled(5, 0);
List<double> decimals = List.filled(5, 0.0);
List<String> characters = List.filled(5, 'a');
@@ -136,9 +136,9 @@
=== "Rust"
```rust title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
let numbers: Vec<i32> = vec![0; 5];
let decimals: Vec<f32> = vec![0.0, 5];
let decimals: Vec<f32> = vec![0.0; 5];
let characters: Vec<char> = vec!['0'; 5];
let bools: Vec<bool> = vec![false; 5];
```
@@ -146,7 +146,7 @@
=== "C"
```c title=""
// 様々な基本データ型を使用して配列を初期化
// さまざまな基本データ型配列を初期化する
int numbers[10];
float decimals[10];
char characters[10];
@@ -156,6 +156,20 @@
=== "Kotlin"
```kotlin title=""
// さまざまな基本データ型で配列を初期化する
val numbers = IntArray(5)
val decinals = FloatArray(5)
val characters = CharArray(5)
val bools = BooleanArray(5)
```
=== "Ruby"
```ruby title=""
# Ruby のリストはさまざまな基本データ型とオブジェクト参照を自由に格納できる
data = [0, 0.0, 'a', false, ListNode(0)]
```
??? pythontutor "実行の可視化"
https://pythontutor.com/render.html#code=class%20ListNode%3A%0A%20%20%20%20%22%22%22%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9%E7%B1%BB%22%22%22%0A%20%20%20%20def%20__init__%28self,%20val%3A%20int%29%3A%0A%20%20%20%20%20%20%20%20self.val%3A%20int%20%3D%20val%20%20%23%20%E8%8A%82%E7%82%B9%E5%80%BC%0A%20%20%20%20%20%20%20%20self.next%3A%20ListNode%20%7C%20None%20%3D%20None%20%20%23%20%E5%90%8E%E7%BB%A7%E8%8A%82%E7%82%B9%E5%BC%95%E7%94%A8%0A%0A%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E7%BB%84%0A%20%20%20%20numbers%20%3D%20%5B0%5D%20*%205%0A%20%20%20%20decimals%20%3D%20%5B0.0%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%AD%97%E7%AC%A6%E5%AE%9E%E9%99%85%E4%B8%8A%E6%98%AF%E9%95%BF%E5%BA%A6%E4%B8%BA%201%20%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%0A%20%20%20%20characters%20%3D%20%5B'0'%5D%20*%205%0A%20%20%20%20bools%20%3D%20%5BFalse%5D%20*%205%0A%20%20%20%20%23%20Python%20%E7%9A%84%E5%88%97%E8%A1%A8%E5%8F%AF%E4%BB%A5%E8%87%AA%E7%94%B1%E5%AD%98%E5%82%A8%E5%90%84%E7%A7%8D%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E5%92%8C%E5%AF%B9%E8%B1%A1%E5%BC%95%E7%94%A8%0A%20%20%20%20data%20%3D%20%5B0,%200.0,%20'a',%20False,%20ListNode%280%29%5D&cumulative=false&curInstr=12&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
@@ -1,87 +1,87 @@
# 文字エンコーディング *
コンピュータシステムでは、すべてのデータ二進形式で格納され、`char`も例外ではありません。文字を表現するために、各文字と二進数の一対一のマッピングを定義する「文字セット」を開発する必要があります。文字セットがあれば、コンピュータは表を参照して二進数を文字に変換できます。
コンピュータでは、すべてのデータ二進数の形で保存されており、文字 `char` も例外ではありません。文字を表すためには、「文字セット」を定義し、各文字と二進数の間の一対一の対応関係を定める必要があります。文字セットがあれば、コンピュータは対応表を参照して二進数から文字への変換を行えます。
## ASCII文字セット
## ASCII 文字セット
<u>ASCIIコード</u>は最も初期の文字セットの一つで、正式にはAmerican Standard Code for Information Interchangeとして知られています。7つの二進1バイトの下位7ビット)を使用して文字を表し、最大128種類の異なる文字を表現できます。以下の図に示すように、ASCIIには英の大文字と小文字、0〜9の数字、様々な句読点、特定の制御文字(改行やタブなど)が含まれています。
<u>ASCII コード</u>は最も早く登場した文字セットで、その正式名称は American Standard Code for Information Interchange(米国標準情報交換コード)です。これは 7 ビットの二進1 バイトの下位 7 ビット)で 1 文字を表し、最大128 種類の異なる文字を表現できます。下図のように、ASCII コードには英の大文字と小文字、数字 0 ~ 9、いくつかの句読点、そしていくつかの制御文字(改行やタブなど)が含まれます。
![ASCIIコード](character_encoding.assets/ascii_table.png)
![ASCII コード](character_encoding.assets/ascii_table.png)
しかし、**ASCIIは英語の文字のみを表現できます**。コンピュータのグローバル化に伴い、より多くの言語を表現するために<u>EASCII</u>と呼ばれる文字セットが開発されました。ASCIIの7ビット構造から8ビット拡張し、256文字表現を可能にしました
しかし、**ASCII コードで表現できるのは英語だけです**。コンピュータのグローバル化に伴い、より多くの言語を表せる <u>EASCII</u> 文字セットが生まれました。これは ASCII の 7 ビットを 8 ビット拡張したもので、256 種類の異なる文字表現できます
世界的に、様々な地域固有のEASCII文字セットが導入されました。これらのセットの最初の128文字はASCIIと一致していますが、残りの128文字は異なる言語の要件に対応するために異なって定義されています。
世界では、さまざまな地域に適した EASCII 文字セットが次々に登場しました。これらの文字セットでは、前半の 128 文字は ASCII コードで統一され、後半の 128 文字は言語の要件に合わせて個別に定義されています。
## GBK文字セット
## GBK 文字セット
後に、**EASCIIでも多くの言語の文字要件を満たすことができない**ことが判明しました。例えば、中国語には約10万の漢字があり、そのうち数千が定期的に使用されています。1980年、中国標準化委員会は6763の中国語文字を含む<u>GB2312</u>文字セットを発表し、中国語のコンピュータ処理ニーズを本質的に満たしました。
その後、人々は**EASCII コードでも多くの言語に必要な文字数を満たせない**ことに気づきました。たとえば漢字は 10 万字近くあり、日常的に使うものだけでも数千字あります。中国国家標準総局は 1980 年に <u>GB2312</u> 文字セットを公開し、6763 字の漢字を収録して、漢字のコンピュータ処理の基本的な需要を満たしました。
しかし、GB2312は一部の稀少文字や繁体字を処理できませんでした。<u>GBK</u>文字セットはGB2312を拡張し、21886の中国語文字を含んでいます。GBKエンコーディングスキームでは、ASCII文字は1バイトで表現され、中国語文字は2バイトを使用します。
しかし、GB2312は一部の珍しい字や繁体字を扱えません。<u>GBK</u> 文字セットは GB2312 を基に拡張されたもので、合計 21886 字の漢字を収録しています。GBKエンコーディング方式では、ASCII 文字は 1 バイト、漢字は 2 バイトで表されます。
## Unicode文字セット
## Unicode 文字セット
コンピュータ技術急速発展と多数の文字セットおよびエンコーディング標準により、数多くの問題が発生しました。一方では、これらの文字セットは一般的に特定の言語の文字のみを定義し、多言語環境では適切に機能できませんでした。方では、同じ言語に対する複数の文字セット標準の存在により、異なるエンコーディング標準を使用するコンピュータ間で情報交換を行う際に文字化けが発生しました。
コンピュータ技術急速発展するにつれて、文字セットと符号化規格は百花繚乱の状態となり、それに伴って多くの問題も生じました。一方では、これらの文字セットは通常、特定の言語の文字しか定義しておらず、多言語環境では正常に動作できませんでした。もう一方では、同じ言語に複数の文字セット規格が存在し、2 台のコンピュータが異なる符号化規格を使っていると、情報伝達の際に文字化けが発生しました。
当時の研究者たちは考えました**世界のすべての言語と記号を含む包括的な文字セットが開発されれば、言語横断環境文字化けに関連する問題を解決できるのではないでしょうか?** このアイデアにインスパイアされて、広範囲な文字セットであるUnicodeが誕生しました。
当時の研究者たちはこう考えました**十分に完全な文字セットを打ち出して、世界中のあらゆる言語と記号をそこに収録すれば、言語環境文字化け問題を解決できるのではない**この発想に後押しされて、大規模で包括的な文字セット Unicode が誕生しました。
<u>Unicode</u>中国語で「统一码」(統一コード)と呼ばれ、理論的に100万文字以上を収容できます。世界中のすべての文字を単一のセットに組み込み、様々な言語の処理表示のための汎用文字セットを提供し、異なるエンコーディング標準による文字化けの問題を減らすことを目指しています。
<u>Unicode</u>中国語名は「統一コード」であり、理論上は 100 万を超える文字を収容できます。Unicode は世界中の文字を 1 つの文字セットに統合することを目指し、さまざまな言語の文字を処理表示できる汎用文字セットを提供することで、符号化規格の違いによる文字化けを減らそうとしています。
1991年のリリース以来、Unicodeは新しい言語と文字を含むよう継続的に拡張されています。2022年9月現在、Unicodeには149,186文字が含まれており、様々な言語の文字、記号、さらには絵文字も含まれています。大なUnicode文字セットでは、一般的に使用される文字は2バイトを占有し、一部の稀少な文字は3バイトまたは4バイトを占有する場合があります。
1991 年の公開以来、Unicode は新しい言語と文字を継続的に拡充してきました。2022 年 9 月時点で、Unicode にはすでに 149186 文字が含まれており、各種言語の文字、記号、さらには絵文字まで収録されています。大な Unicode 文字セットでは、よく使われる文字は 2 バイトを占、一部の珍しい文字は 3 バイト、さらには 4 バイトを占めます。
Unicodeは各文字に数値(「コードポイント」と呼ばれる)を割り当てる汎用文字セットですが、**れらの文字コードポイントコンピュータシステムにどのように格納されるべきかは指定していません**。疑問が生じるかもしれません:システムはテキスト内の異なる長さのUnicodeコードポイントをどのように解釈するのでしょうか?例えば、2バイトのコードが与えられた場合、システムはそれが単一の2バイト文字を表すのか、2つの1バイト文字を表すのかをどのように判断するのでしょうか
Unicode は汎用文字セットであり、本質的には各文字に番号(「コードポイント」)を割り当てるものですが、**れらのコードポイントコンピュータ内でどのように保存するかまでは規定していません**。ここで疑問が生じます。長さの異なる Unicode コードポイントが同じテキストに現れたとき、システムはどのように文字を解析するのでしょうか。たとえば長さ 2 バイトの符号が与えられたとき、それが 2 バイトの 1 文字なのか、1 バイトの 2 文字なのかをどう判定するのでしょうか
**この問題に対する簡単な解決策は、すべての文字を等長エンコーディングとして格納することです**。以下の図に示すように、「Hello」の各文字は1バイトを占有し、「算法」(アルゴリズムの各文字は2バイトを占有します。上位ビットをゼロで埋めることで、「Hello 算法」のすべての文字を2バイトとしてエンコードできます。この方法により、システムは2バイトごとに文字を解釈し、フレーズの内容を復元できます。
この問題に対して、**すべての文字を固定長の符号として保存する**という直接的な解決策があります。下図のように、「Hello」の各文字は 1 バイト、「アルゴリズムの各文字は 2 バイトを占ます。上位ビットを 0 で埋めることで、「Hello アルゴリズム」のすべての文字を 2 バイト長にエンコードできます。こうすれば、システムは 2 バイトごとに 1 文字を解析して、この語句の内容を復元できます。
![Unicodeエンコーディング例](character_encoding.assets/unicode_hello_algo.png)
![Unicode エンコーディング](character_encoding.assets/unicode_hello_algo.png)
しかしASCIIが示したように、英語のエンコーディングには1バイトのみが必要です。上記のアプローチを使用すると、英語テキストが占有する空間ASCIIエンコーディングと比較して2倍になり、メモリ空間の無駄になります。したがって、より効率的なUnicodeエンコーディング方が必要です。
しかし ASCII コードはすでに、英語の符号化には 1 バイトで十分であることを示しています。上記の方式を採用すると、英語テキストが占る空間ASCII エンコーディング時の 2 倍になり、メモリ空間の浪費が大きくなります。そのため、より効率的な Unicode エンコーディング方が必要です。
## UTF-8エンコーディング
## UTF-8 エンコーディング
現在、UTF-8は国際的に最も広く使用されているUnicodeエンコーディング方になっています。**これは可変長エンコーディング**で、文字の複雑さに応じて1〜4バイトを使用して文字を表現します。ASCII文字は1バイトのみが必要で、ラテン文字とギリシャ文字は2バイト、一般的に使用される中国語文字は3バイト、その他の稀少な文字は4バイト必要です。
現在、UTF-8 は国際的に最も広く使れている Unicode エンコーディング方になっています。**これは可変長エンコーディング**であり、1 文字を 1 〜 4 バイトで表し、文字の複雑さに応じて長さが変わります。ASCII 文字は 1 バイト、ラテン文字とギリシャ文字は 2 バイト、一般的な漢字は 3 バイト、そのほかの一部の珍しい文字は 4 バイト必要です。
UTF-8のエンコーディング規則は複雑ではなく、2つのケースに分けることができます
UTF-8 の符号化規則はそれほど複雑ではなく、次の 2 つのケースに分けられます
- 1バイト文字の場合、最上位ビットを$0$に設定し、残りの7ビットをUnicodeコードポイントに設定します。注目すべきは、ASCII文字がUnicodeセットの最初の128コードポイントを占有することです。これは**UTF-8エンコーディングASCIIと後方互換性がある**ことを意味します。これは、UTF-8を使用して古いASCIIテキストを解析できることを意味します。
- 長さ$n$バイトの文字($n > 1$の場合、最初のバイトの上位$n$ビットを$1$に設定し、$(n + 1)^{\text{th}}$ビットを$0$に設定します。2番目のバイトから、各バイトの上位2ビットを$10$に設定します。残りのビットはUnicodeコードポイントを埋めるために使用されます。
- 長さ 1 バイト文字では、最上位ビットを $0$ にし、残りの 7 ビットを Unicode コードポイントに設定します。ここで注意すべきなのは、ASCII 文字が Unicode 文字セットの先頭 128 個のコードポイントを占めていることです。つまり、**UTF-8 エンコーディングASCII コードと下位互換性があります**。このため、UTF-8 を使て古い ASCII コードのテキストを解析できます。
- 長さ $n$ バイトの文字(ただし $n > 1$では、先頭バイトの上位 $n$ ビットをすべて $1$ にし、第 $n + 1$ ビットを $0$ に設定します。2 バイト目以降では、各バイトの上位 2 ビットをいずれも $10$ にし、残りのすべてのビットで文字の Unicode コードポイントを埋めます。
以下の図は「Hello算法」のUTF-8エンコーディングを示しています。上位$n$ビットが$1$に設定されているため、システムは最上位ビットで$1$に設定されたビット数を数えることで文字の長さ$n$として決定できることが観察できます。
図は「Helloアルゴリズム」に対応する UTF-8 エンコーディングを示しています。観察すると、上位 $n$ ビットがすべて $1$ に設定されているため、システムは先頭から連続する $1$ の個数を読むことで、その文字の長さ$n$ であると解析できます。
しかし、なぜ残りのバイトの上位2ビットを$10$に設定するのでしょうか?実際、この$10$は一種のチェックサムとして機能します。システムが間違ったバイトからテキスト解析を開始した場合、バイト先頭の$10$によりシステムは異常を迅速に検出できます。
では、なぜ残りのすべてのバイトの上位 2 ビットを $10$ にするのでしょうか。実は、この $10$ は検査用の印として機能します。システムがったバイト位置からテキスト解析し始めたとしても、バイト先頭の $10$ によって異常を素早く判定できます。
$10$をチェックサムとして使用する理由は、UTF-8エンコーディング規則の下では、文字の最上位2ビットが$10$になることは不可能だからです。これは矛盾により証明できます文字の上位2ビットが$10$の場合、文字の長さ$1$であることを示し、これはASCIIに対応します。しかしASCII文字の最上位ビットは$0$であるべきで、これは仮定と矛盾します。
この $10$ を検査用の印とする理由は、UTF-8 の符号化規則では上位 2 ビットが $10$ になる文字は存在しないからです。この結論は背理法で証明できます。ある文字の上位 2 ビットが $10$ だと仮定すると、その文字の長さ$1$ であり、ASCII コードに対応することになります。しかし ASCII コードの最上位ビットは $0$ であるはずなので、仮定と矛盾します。
![UTF-8エンコーディング例](character_encoding.assets/utf-8_hello_algo.png)
![UTF-8 エンコーディング](character_encoding.assets/utf-8_hello_algo.png)
UTF-8以外にも、他の一般的なエンコーディング方法には以下があります
UTF-8 以外にも、一般的なエンコーディング方式として次の 2 つがあります
- **UTF-16エンコーディング**2または4バイトを使用して文字を表現します。すべてのASCII文字と一般的に使用される非英語文字は2バイトで表現され、少数の文字は4バイト必要す。2バイト文字の場合、UTF-16エンコーディングはUnicodeコードポイントと等しくなります。
- **UTF-32エンコーディング**すべての文字が4バイトを使用します。これは、UTF-32UTF-8UTF-16よりも多くの空間を占有することを意味し、特にASCII文字の割合が高いテキストで顕著です。
- **UTF-16 エンコーディング**1 文字を 2 バイトまたは 4 バイトで表します。すべての ASCII 文字と一般的非英語文字は 2 バイトで表し、一部の文字だけが 4 バイト必要とします。2 バイト文字については、UTF-16 エンコーディングは Unicode コードポイントと等しくなります。
- **UTF-32 エンコーディング**各文字を必ず 4 バイトで表します。つまり UTF-32UTF-8UTF-16 よりも多くの領域を消費し、とくに ASCII 文字の比率が高いテキストでその傾向が顕著です。
ストレージ空間の観点から、UTF-8を使用して英語文字表現することは1バイトのみが必要なため非常に効率的です。UTF-16を使用して一部の非英語文字(中国語など)をエンコードすることは、2バイトのみが必要なためより効率的になる場合があります。一方、UTF-8では3バイト必要になる場合があります。
記憶領域の使用量という観点では、UTF-8英語文字表現に非常に効率的で、必要なのは 1 バイトだけです。一方、UTF-16一部の非英語文字(たとえば中国語の文字)の符号化でより効率的になることがあり、必要なのは 2 バイトだけで、UTF-8 では 3 バイト必要になる場合があります。
互換性の観点から、UTF-8は最も汎用性があり、多くのツールライブラリがUTF-8を優先的にサポートしています。
互換性という観点では、UTF-8汎用性が最も高く、多くのツールライブラリが UTF-8 を優先的にサポートしています。
## プログラミング言語における文字エンコーディング
## プログラミング言語文字エンコーディング
歴史的に、多くのプログラミング言語はプログラム実行中の文字列処理にUTF-16UTF-32などの固定長エンコーディングを利用していました。これにより文字列を配列として処理でき、いくつかの利点があります
従来の多くのプログラミング言語では、実行中の文字列に UTF-16UTF-32 のような固定長エンコーディングが使われています。固定長エンコーディングでは、文字列を配列のように扱えるため、次のような利点があります
- **ランダムアクセス**UTF-16でエンコードされた文字列は簡単にランダムアクセスできます。可変長エンコーディングであるUTF-8の場合、$i^{th}$文字の位置を特定するには文字列の開始から$i^{th}$位置まで走査する必要があり、$O(n)$時間がかかります。
- **文字数カウント**:ランダムアクセスと同様に、UTF-16でエンコードされた文字列の文字数をカウントすることは$O(1)$操作です。しかし、UTF-8でエンコードされた文字列の文字数をカウントするには文字列全体を走査する必要があります。
- **文字列操作**分割、連結、挿入、削除などの多くの文字列操作は、UTF-16でエンコードされた文字列で簡単です。これらの操作は一般的に、UTF-8エンコーディングの有効性を確保するためにUTF-8でエンコードされた文字列で追加計算が必要です。
- **ランダムアクセス**UTF-16 で符号化された文字列はランダムアクセスが容易です。UTF-8 は可変長エンコーディングなので、第 $i$ 文字を見つけるには文字列の先頭から第 $i$ 文字まで走査する必要があり、$O(n)$時間がかかります。
- **文字数の計算**:ランダムアクセスと同様に、UTF-16 で符号化された文字列の長さを計算するのも $O(1)$操作です。しかし、UTF-8 で符号化された文字列の長さを計算するには文字列全体を走査する必要があります。
- **文字列操作**UTF-16 で符号化された文字列では、多くの文字列操作(分割、連結、挿入、削除など)をより簡単に行えます。UTF-8 で符号化された文字列では、これらの操作を行う際に、無効な UTF-8 エンコーディングを生じさせないための追加計算が通常必要になります。
プログラミング言語における文字エンコーディングスキームの設計は、様々な要因を含む興味深いトピックです:
実際、プログラミング言語における文字エンコーディング方式の設計は、とても興味深い話題であり、多くの要因が関わっています。
- Java`String`型はUTF-16エンコーディングを使用し、各文字が2バイトを占有します。これは16ビットがすべての可能な文字を表現するのに十分であるという初期の信念に基づいており、後に間違いであることが証明されました。Unicode標準が16ビットを超えて拡張されると、Javaの文字は「サロゲートペア」として知られる16ビット値のペアで表される場合があります。
- JavaScriptTypeScriptは、Javaと同様の理由でUTF-16エンコーディングを使用します。JavaScriptが1995年にNetscapeによって最初に導入されたとき、Unicodeはまだ初期段階にあり、16ビットエンコーディングはすべてのUnicode文字を表現するのに十分でした。
- C#はUTF-16エンコーディングを使用し、これは主にMicrosoftによって設計された.NETプラットフォーム、および多くのMicrosoft技術(Windowsオペレーティングシステムを含む)UTF-16エンコーディングを広範囲に使用しているためです。
- Java`String` 型は UTF-16 エンコーディングを使用し、各文字は 2 バイトを占ます。これは Java 言語の設計当初、人々が 16 ビットあればあらゆる文字を表現するのに十分だと考えていたためです。しかし、これは誤った判断でした。その後 Unicode 規格は 16 ビットを超える範囲へ拡張されたため、現在の Java では 1 文字が 16 ビット値の組(「サロゲートペア」)で表されることがあります。
- JavaScriptTypeScript の文字列が UTF-16 エンコーディングを使う理由も Java と似ています。1995 年に Netscape 社が初めて JavaScript 言語を公開した当時、Unicode はまだ発展初期にあり、16 ビットの符号化で十分すべての Unicode 文字を表せると考えられていました。
- C# が UTF-16 エンコーディングを使う主な理由は、.NET プラットフォームが Microsoft によって設計されMicrosoft の多くの技術(Windows オペレーティングシステムを含む)UTF-16 エンコーディングが広く使われているためです。
文字数過小評価により、これらの言語は16ビットを超えるUnicode文字を表現するために「サロゲートペア」を使用する必要がありました。このアプローチには欠点があります:サロゲートペアを含む文字列は2バイトまたは4バイトを占有する文字を持つ場合があり、固定長エンコーディングの利点を失います。さらに、サロゲートペアの処理はプログラミング複雑さとデバッグの困難さを追加します。
以上のプログラミング言語は文字数過小評価していたため、16 ビットを超える長さの Unicode 文字を表ために「サロゲートペア」を採用せざるを得ませんでした。これはやむを得ない妥協策です。一方では、サロゲートペアを含む文字列では、1 文字が 2 バイトまたは 4 バイトを占める可能性があり、固定長エンコーディングの利点が失われます。もう一方では、サロゲートペアの処理には追加のコードが必要となり、プログラミング複雑さとデバッグの難しさが増します。
れらの課題に対処するため、一部の言語は代替エンコーディング戦略を採用しています:
うした理由から、一部のプログラミング言語では別のエンコーディング方式が採用されました。
- Python`str`型は、文字のストレージ長が文字列内の最大のUnicodeコードポイントに依存する柔軟な表現でUnicodeエンコーディングを使用します。すべての文字がASCIIの場合、各文字は1バイトを占有し、基本多言語面(BMP)内の文字は2バイト、BMPを超える文字は4バイトを占有します。
- Go`string`型は内部的にUTF-8エンコーディングを使用します。Goは個別のUnicodeコードポイントを表現するための`rune`型も提供します。
- Rust`str``String`型は内部的にUTF-8エンコーディングを使用します。Rustは個別のUnicodeコードポイント用の`char`型も提供します。
- Python`str` は Unicode エンコーディングを使用し、柔軟な文字列表現を採用しています。保存される文字の長さは、その文字列中で最大の Unicode コードポイントに依存します。文字列がすべて ASCII 文字であれば各文字は 1 バイト、ASCII の範囲を超える文字があってもすべてが基本多言語面(BMP)内であれば各文字は 2 バイト、BMP を超える文字があれば各文字は 4 バイトを占ます。
- Go 言語の `string` 型は内部UTF-8 エンコーディングを使用します。Go 言語には単一の Unicode コードポイントを表`rune` 型も用意されています。
- Rust 言語の `str``String` 型は内部UTF-8 エンコーディングを使用します。Rust にも単一の Unicode コードポイントを表す `char` 型があります。
上記の議論は、プログラミング言語での文字列の格納方法に関するものであり、**ファイルでの文字列の格納方法やネットワーク上での送信方法とは異なる**ことに注意することが重要です。ファイルストレージやネットワーク送では、文字列は通常、最適な互換性と空間効率のためにUTF-8形式エンコードされます。
注意すべきなのは、ここまでの議論はすべて、プログラミング言語での文字列の保存方法についてであり、**文字列をファイルに保存したりネットワークで転送したりする方法とは別の問題である**ということです。ファイル保存やネットワーク送では、通常、互換性と空間効率を最適化するために文字列を UTF-8 形式エンコードます。
@@ -1,48 +1,48 @@
# データ構造の分類
一般的なデータ構造には、配列、連結リスト、スタック、キュー、ハッシュ、木、ヒープ、グラフがあります。これらは「論理構造」と「物理構造」分類できます。
代表的なデータ構造には、配列、連結リスト、スタック、キュー、ハッシュテーブル、木、ヒープ、グラフがありこれらは「論理構造」と「物理構造」の 2 つの観点から分類できます。
## 論理構造:線形と非線形
**論理構造はデータ要素間の論理的関係を明らかにします**。配列と連結リストでは、データは定の順序で配置され、データ間の線形関係を示しています。一方、木ではデータは上から下へ階層的に配置され、「祖先」と「子孫」の派生関係を示しています。そして、グラフはノードとエッジから構成され、複雑なネットワーク関係を反映しています。
**論理構造はデータ要素間の論理的関係をします**。配列と連結リストでは、データは定の順序で並び、データ間の線形関係を表します。一方、木ではデータは上から下へ階層的に並び、「祖先」と「子孫」の派生関係を示します。グラフはノードと辺で構成され、複雑なネットワーク関係を反映します。
下図に示されているように、論理構造は「線形」と「非線形」の2つの主要カテゴリに分けることができます。線形構造はより直感的で、データが論理関係において線形に配置されていることを示しています。非線形構造は、逆に非線形に配置されています。
以下の図に示すように、論理構造は「線形」と「非線形」の 2 つに大別できます。線形構造は比較的直感的で、データが論理関係において線形に並ぶことを指します。非線形構造はその逆で、非線形に配置されます。
- **線形データ構造**:配列、連結リスト、スタック、キュー、ハッシュ表。要素が一対一の順関係を持ちます。
- **非線形データ構造**:木、ヒープ、グラフ、ハッシュ
- **線形データ構造**:配列、連結リスト、スタック、キュー、ハッシュテーブルであり、要素間は 1 対 1 の順関係す。
- **非線形データ構造**:木、ヒープ、グラフ、ハッシュテーブル
非線形データ構造は、さらに木構造とネットワーク構造に分けることができます。
非線形データ構造は、さらに木構造と網状構造に分けられます。
- **木構造**:木、ヒープ、ハッシュ表。要素が一対多の関係を持ちます。
- **ネットワーク構造**:グラフ。要素が多対多の関係を持ちます。
- **木構造**:木、ヒープ、ハッシュテーブルであり、要素間は 1 対多の関係す。
- **網状構造**:グラフであり、要素間は多対多の関係す。
![Linear and non-linear data structures](classification_of_data_structure.assets/classification_logic_structure.png)
![線形データ構造と非線形データ構造](classification_of_data_structure.assets/classification_logic_structure.png)
## 物理構造:連続と分散
**アルゴリズムの実行中、処理されるデータはメモリに格納されます**。下図はコンピュータのメモリスティックを示しており、各黒い正方形は物理メモリ空間です。メモリ巨大なExcelスプレッドシートと考えることができ、各セルは一定量のデータを格納できます。
**アルゴリズムのプログラムが実行されるとき、処理中のデータは主にメモリに格納されます**。下図はコンピュータのメモリモジュールを示しており、各黒い四角はそれぞれ 1 つのメモリ空間を表しています。メモリ巨大な Excel の表のようなものだと考えることができ、各セルは一定量のデータを格納できます。
**システムはメモリアドレスによって目標位置のデータアクセスします**。下図に示されているように、コンピュータは特定のルールに従って表の各セルに一意の識別子を割り当て、各メモリ空間が一意のメモリアドレスを持つことを保証します。これらのアドレスにより、プログラムはメモリに格納されたデータにアクセスできます。
**システムはメモリアドレスを通じて目的の位置にあるデータアクセスします**。下図に示ように、コンピュータは特定の規則に従って表の各セルに番号を割り当て、各メモリ空間が一意のメモリアドレスを持つようにします。これらのアドレスがあれば、プログラムはメモリ内のデータにアクセスできます。
![Memory stick, memory spaces, memory addresses](classification_of_data_structure.assets/computer_memory_location.png)
![メモリモジュール、メモリ空間、メモリアドレス](classification_of_data_structure.assets/computer_memory_location.png)
!!! tip
メモリをExcelスプレッドシートに比較することは簡略化された類推であることに注意してください。メモリの実際の動作メカニズムはより複雑で、アドレス空間、メモリ管理、キャッシュメカニズム、仮想メモリ、物理メモリなどの概念が関係しています。
補足すると、メモリを Excel の表にたとえるのは単純化した比喩であり、実際のメモリの動作機構はより複雑で、アドレス空間、メモリ管理、キャッシュ機構、仮想メモリ、物理メモリなどの概念が関わります。
メモリはすべてのプログラム共有リソースです。あるメモリブロックが1つのプログラムによって占有されると、他のプログラムが同時に使用することはできません。**したがって、メモリリソースはデータ構造とアルゴリズムの設計における重要な考慮事項です**。えば、アルゴリズムのピークメモリ使用量は、システムの残り空きメモリを超えてはいけません。連続したメモリブロックが不足している場合は、非連続メモリブロックに格納できるデータ構造を選択する必要があります。
メモリはすべてのプログラム共有される資源であり、あるメモリ領域が 1 つのプログラムに占有されると、通常は他のプログラムが同時に利用できません。**したがって、データ構造とアルゴリズムの設計では、メモリ資源は重要な考慮要素です**。たとえば、アルゴリズムが使用するメモリ使用量のピークは、システムに残っている空きメモリを超えてはなりません。大きな連続メモリ領域が不足している場合、選択するデータ構造は分散したメモリ空間に格納できる必要があります。
下図に示されているように、**物理構造はコンピュータメモリにおけるデータの格納方法を反映し**、連続空間格納(配列)と非連続空間格納(連結リスト)に分けることができます。2つのタイプの物理構造は時間効率と空間効率の観点で補完的な特性を示します。
下図に示ように、**物理構造はデータがコンピュータメモリ内にどのように格納されるかを表します**。これは連続空間への格納(配列)と分散空間への格納(連結リスト)に分けられます。物理構造は低レベルでデータのアクセス、更新、追加、削除などの操作方法を決定し、2 種類の物理構造は時間効率と空間効率の面で相補的な特徴を持ちます。
![Contiguous space storage and dispersed space storage](classification_of_data_structure.assets/classification_phisical_structure.png)
![連続空間格納と分散空間格納](classification_of_data_structure.assets/classification_phisical_structure.png)
**すべてのデータ構造は配列、連結リスト、またはその組み合わせに基づいて実装されていることに注意してください**。例えば、スタックとキューは配列または連結リストのどちらでも実装できます。ハッシュの実装には配列と連結リストの両方が関係する場合があります。
補足すると、**すべてのデータ構造は配列、連結リスト、またはその両者の組み合わせに基づいて実装されます**。たとえば、スタックとキューは配列でも連結リストでも実装できます。一方、ハッシュテーブルの実装には配列と連結リストの両方が含まれる場合があります。
- **配列ベースの実装**:スタック、キュー、ハッシュ、木、ヒープ、グラフ、行列、テンソル(次元$\geq 3$の配列)。
- **連結リストベースの実装**:スタック、キュー、ハッシュ、木、ヒープ、グラフなど。
- **配列に基づいて実装可能**:スタック、キュー、ハッシュテーブル、木、ヒープ、グラフ、行列、テンソル(次元 $\geq 3$ の配列)など
- **連結リストに基づいて実装可能**:スタック、キュー、ハッシュテーブル、木、ヒープ、グラフなど。
配列に基づいて実装されたデータ構造は「静的データ構造」とも呼ばれ初期化後に長さを変更できないことを意味します。逆に、連結リストに基づいたものは「動的データ構造」と呼ばれ、プログラム実行中にサイズを調整できます。
連結リストは初期化後も、プログラムの実行中に長さを調整できるため、「動的データ構造」とも呼ばれます。配列は初期化後に長さを変更できないため、「静的データ構造」と呼ばれます。なお、配列もメモリを再割り当てすることで長さを変更でき、ある程度の「動的性」を持たせることができます。
!!! tip
物理構造理解するのが困難な場合は、次の章「配列と連結リスト」を読んでから、この節に戻ることを勧めます。
物理構造理解が難しいと感じる場合は、先に次の章を読んでから本節を振り返ることを勧めます。
+4 -4
View File
@@ -1,9 +1,9 @@
# データ構造
![Data structures](../assets/covers/chapter_data_structure.jpg)
![データ構造](../assets/covers/chapter_data_structure.jpg)
!!! abstract
データ構造は堅牢で多様なフレームワークとして機能します
データの整然とした組織化のための設計図を提供し、その上でアルゴリズム生き生きと動き出します。
データ構造は、堅固で多様な枠組みのようなものである
それはデータを秩序立てて組織するための青写真を示し、アルゴリズムはその上で生き生きと動き出す。
@@ -2,23 +2,23 @@
!!! tip
本書では、アスタリスク「*」が付いた章は任意読書です。時間が不足している場合や難しいと感じる場合は、最初はこれらをスキップして、必須の章を完了した後に戻ることができます
本書では、タイトルに * 記号が付いている章は選読です。時間が限られている場合や理解が難しいと感じる場合は、いったん読み飛ばし、必読章を終えてから個別に取り組んでください
## 整数エンコーディング
## 符号付き絶対値表現、1 の補数、2 の補数
節の表、すべての整数型は正数よりも1つ多い負の数を表現できることを観察しました。例えば`byte`の範囲は$[-128, 127]$です。この現象は直感に反するように見え、その根本的な理由には符号絶対値、1の補数、2の補数エンコーディングの知識が関与しています。
前節の表を見ると、すべての整数型で表せる負数の個数は正数より 1 つ多く、たとえば `byte` の値域は $[-128, 127]$ です。この現象は直感に反するように見えますが、その背景には符号付き絶対値表現、1 の補数、2 の補数に関する知識があります。
まず重要なことは、**数値はコンピュータ内で2の補数形式で格納される**ということです。なぜそうなのかを分析する前に、これら3つのエンコーディング方法を定義しましょう:
まず押さえておくべきなのは、**数値はコンピュータ内で「2 の補数」の形で保存される**ということです。その理由を説明する前に、まずはこの 3 つの定義を示します。
- **符号絶対値**:数値の二進表現の最上位ビットを符号ビットとし、$0$は正数、$1$は負数を表します。残りのビット数値の値を表します。
- **1の補数**:正数の1の補数は符号絶対値と同じです。負の数の場合、符号ビット以外のすべてのビットを反転して得られます。
- **2の補数**:正数の2の補数は符号絶対値と同じです。負の数の場合、その1の補数に$1$を加えて得られます。
- **符号付き絶対値表現**:数値の二進表現の最上位ビットを符号ビットとみなし、$0$ は正数、$1$ は負数を表し残りのビット数値の値を表します。
- **1 の補数**:正数の 1 の補数は符号付き絶対値表現と同じで、負数の 1 の補数は符号ビットを除くすべてのビットを反転したものです。
- **2 の補数**:正数の 2 の補数は符号付き絶対値表現と同じで、負数の 2 の補数は 1 の補数に $1$ を加えたものです。
以下の図は、符号絶対値、1の補数、2の補数の変換を示しています
図は、符号付き絶対値表現、1 の補数、2 の補数の変換方法を示しています
![符号絶対値、1の補数、2の補数の変換](number_encoding.assets/1s_2s_complement.png)
![符号付き絶対値表現、1 の補数、2 の補数の相互変換](number_encoding.assets/1s_2s_complement.png)
<u>符号絶対値</u>は最も直感的ですが、制限があります。一つには、**符号絶対値の負の数は計算で直接使用できません**。えば符号絶対値$1 + (-2)$を計算すると$-3$になり、これは正しくありません
<u>符号付き絶対値表現(sign-magnitude</u>は最も直感的ですが、いくつかの制約があります。まず、**負数の符号付き絶対値表現はそのまま演算に使えません**。たとえば符号付き絶対値表現で $1 + (-2)$ を計算すると、結果は $-3$ になってしまい、これは明らかに誤りです
$$
\begin{aligned}
@@ -29,20 +29,20 @@ $$
\end{aligned}
$$
この問題に対処するため、コンピュータは<u>1の補数</u>導入ました。1の補数に変換して$1 + (-2)$を計算し、結果を符号絶対値に戻すと、正しい結果$-1$が得られます。
この問題を解決するため、コンピュータは<u>1 の補数1's complement</u>導入されました。まず符号付き絶対値表現を 1 の補数に変換し、1 の補数で $1 + (-2)$ を計算してから、結果を 1 の補数から符号付き絶対値表現へ戻すと、正しい結果 $-1$ が得られます。
$$
\begin{aligned}
& 1 + (-2) \newline
& \rightarrow 0000 \; 0001 \; \text{(符号絶対値)} + 1000 \; 0010 \; \text{(符号絶対値)} \newline
& = 0000 \; 0001 \; \text{(1の補数)} + 1111 \; 1101 \; \text{(1の補数)} \newline
& = 1111 \; 1110 \; \text{(1の補数)} \newline
& = 1000 \; 0001 \; \text{(符号絶対値)} \newline
& \rightarrow 0000 \; 0001 \; \text{(符号付き絶対値表現)} + 1000 \; 0010 \; \text{(符号付き絶対値表現)} \newline
& = 0000 \; 0001 \; \text{(1 の補数)} + 1111 \; 1101 \; \text{(1 の補数)} \newline
& = 1111 \; 1110 \; \text{(1 の補数)} \newline
& = 1000 \; 0001 \; \text{(符号付き絶対値表現)} \newline
& \rightarrow -1
\end{aligned}
$$
また、**符号絶対値では0に2つの表があります**:$+0$と$-0$です。これは0に対して2つの異なる二進エンコーディングがあることを意味し、曖昧さを引き起こす可能性があります。えば条件チェックで正と負の0を区別しないと、正しくない結果になる可能性があります。この曖昧さに対処するには追加のチェックが必要で、計算効率が低下する可能性があります。
一方、**数値 0 の符号付き絶対値表現には $+0$ と $-0$ の 2 つの表し方があります**。つまり、数値 0 に対して異なる 2 つの二進コードが対応しており、これは曖昧さの原因になります。たとえば条件判定で正のゼロと負のゼロを区別しないと、誤った判定結果になる可能性があります。また、この曖昧さを解消しようとすると追加の判定処理が必要になり、計算効率が下がるおそれがあります。
$$
\begin{aligned}
@@ -51,100 +51,100 @@ $$
\end{aligned}
$$
符号絶対値と同様に、1の補数も正負の0の曖昧さに悩まされます。そのため、コンピュータはさらに<u>2の補数</u>を導入しました。符号絶対値、1の補数、2の補数における負の0の変換過程を観察してみましょう
符号付き絶対値表現と同様に、1 の補数も正負のゼロの曖昧さがあります。そこでコンピュータはさらに<u>2 の補数2's complement</u>を導入しました。まずは負のゼロについて、符号付き絶対値表現、1 の補数、2 の補数の変換を見てみましょう
$$
\begin{aligned}
-0 \rightarrow \; & 1000 \; 0000 \; \text{(符号絶対値)} \newline
= \; & 1111 \; 1111 \; \text{(1の補数)} \newline
= 1 \; & 0000 \; 0000 \; \text{(2の補数)} \newline
-0 \rightarrow \; & 1000 \; 0000 \; \text{(符号付き絶対値表現)} \newline
= \; & 1111 \; 1111 \; \text{(1 の補数)} \newline
= 1 \; & 0000 \; 0000 \; \text{(2 の補数)} \newline
\end{aligned}
$$
負の0の1の補数に$1$を加えると桁上がりが発生しますが、`byte`の長さは8ビットのみのため、9番目のビットへの桁上がり$1$は破棄されます。したがって、**負の0の2の補数は$0000 \; 0000$**で、正の0と同じになり、曖昧さが解決されます。
負のゼロの 1 の補数に $1$ を加えると桁上がりが発生しますが、`byte`の長さは 8 ビットしかないため、第 9 ビットへあふれた $1$ は捨てられます。つまり、**負のゼロの 2 の補数は $0000 \; 0000$ であり、正のゼロの 2 の補数と同じです**。そのため、2 の補数表現ではゼロは 1 つしか存在せず、正負のゼロの曖昧さは解消されます。
最後の謎は、`byte`$[-128, 127]$の範囲で、追加の負の数$-128$があることです。$[-127, +127]$の区間では、すべての整数に対応する符号絶対値、1の補数、2の補数があり、相互変換が可能であることを観察します。
最後にもう 1 つ疑問が残ります。`byte` 型の値域は $[-128, 127]$ ですが、余分にある負数 $-128$ はどのように得られるのでしょうか。区間 $[-127, +127]$ にあるすべての整数には、それぞれ対応する符号付き絶対値表現、1 の補数、2 の補数があり、符号付き絶対値表現と 2 の補数の間は相互変換できます。
しかし、**2の補数$1000 \; 0000$対応する符号絶対値を持たない例外です**。変換方法によると、その符号絶対値は$0000 \; 0000$で、0を示します。これは矛盾を示しています。なぜなら、その2の補数は自分自身を表すべきだからです。コンピュータは、この特別な2の補数$1000 \; 0000$$-128$を表すものとして指定しています。実際、2の補数での$(-1) + (-127)$の計算結果は$-128$になります。
しかし、**2 の補数 $1000 \; 0000$ だけは例外で、対応する符号付き絶対値表現を持ちません**。変換規則に従うと、この 2 の補数に対応する符号付き絶対値表現は $0000 \; 0000$ になります。これは明らかに矛盾しています。なぜなら、この符号付き絶対値表現は数値 $0$ を表し、その 2 の補数は自分自身であるはずだからです。コンピュータは、この特別な 2 の補数 $1000 \; 0000$$-128$ と定めています。実際、2 の補数での $(-1) + (-127)$ の計算結果はちょうど $-128$ になります。
$$
\begin{aligned}
& (-127) + (-1) \newline
& \rightarrow 1111 \; 1111 \; \text{(符号絶対値)} + 1000 \; 0001 \; \text{(符号絶対値)} \newline
& = 1000 \; 0000 \; \text{(1の補数)} + 1111 \; 1110 \; \text{(1の補数)} \newline
& = 1000 \; 0001 \; \text{(2の補数)} + 1111 \; 1111 \; \text{(2の補数)} \newline
& = 1000 \; 0000 \; \text{(2の補数)} \newline
& \rightarrow 1111 \; 1111 \; \text{(符号付き絶対値表現)} + 1000 \; 0001 \; \text{(符号付き絶対値表現)} \newline
& = 1000 \; 0000 \; \text{(1 の補数)} + 1111 \; 1110 \; \text{(1 の補数)} \newline
& = 1000 \; 0001 \; \text{(2 の補数)} + 1111 \; 1111 \; \text{(2 の補数)} \newline
& = 1000 \; 0000 \; \text{(2 の補数)} \newline
& \rightarrow -128
\end{aligned}
$$
お気づきかもしれませんが、これらの計算はすべて加算であり、重要な事実を示しています**コンピュータ内部ハードウェア回路は主に加算演算を中心に設計されています**。これは、加算が乗算、除算、減算などの他の演算と比較してハードウェアで実装しやすく、並列化が容易で高速計算が可能だからです。
すでにお気づきかもしれませんが、の計算はすべて加算です。これは重要な事実を示しています**コンピュータ内部ハードウェア回路は、主として加算を基準に設計されている**のです。なぜなら、加算はほかの演算(乗算、除算、減算など)に比べてハードウェアで実装しやすく、並列化もしやすく、演算速度も速いからです。
これはコンピュータが加算のみを実行できることを意味するものではありません。**加算と基本的な論理演算を組み合わせることで、コンピュータは様々な他の数学演算を実できます**。えば減算$a - b$$a + (-b)$に変換でき、乗算除算は複数の加算または減算に変換できます。
ただし、これはコンピュータが加算しかできないという意味ではありません。**加算といくつかの基本的な論理演算を組み合わせることで、コンピュータはさまざまな数学演算を実できます**。たとえば減算 $a - b$ は加算 $a + (-b)$ に変換できますし、乗算除算も繰り返しの加算または減算に変換できます。
コンピュータで2の補数を使用する理由をまとめることができます:2の補数表現により、コンピュータは同じ回路と演算を使用して正と負数の加算を処理でき、減算用の特別なハードウェア回路の必要性を排除し、正負の0の曖昧さを回避できます。これによりハードウェア設計大幅に簡化され、算効率向上します。
これで、コンピュータが 2 の補数を使理由をまとめられます。2 の補数表現に基づけば、コンピュータは同じ回路と操作で正数と負数の加算を扱うことができ、減算用の特別なハードウェア回路を設計する必要がなく、正負のゼロの曖昧さも特別に処理しなくて済みます。これによりハードウェア設計大幅に簡化され、算効率向上します。
2の補数の設計は非常に巧妙で、スペースの制約により、ここで停止します。興味のある読者はさらに探求することを奨励します
2 の補数の設計は非常に巧妙ですが、紙幅の都合上ここまでにします。興味のある読者はさらに深く調べてみてください
## 浮動小数点数エンコーディング
## 浮動小数点数エンコーディング
興味深いことに気づいたかもしれません:同じ4バイトの長さにもかかわらず、なぜ`float``int`と比較してはるかに大きい値の範囲を持つのでしょうかこれは直感に反するように見えます。`float`は分数を表現する必要があるため、範囲が縮小すると予想されるからです。
注意深い人なら気づくかもしれません。`int``float` はどちらも長さが 4 バイトで同じなのに、なぜ `float` の値域は `int` よりはるかに広いのでしょうかこれはかなり直感に反します。というのも、`float` は小数を表す必要があるので、本来なら値域は狭くなるはずだからです。
実際、**これは浮動小数点数`float`)で使用される異なる表現方法によるものです**。32ビットの二進数を次のように考えてみましょう:
実際には、**これは浮動小数点数 `float` が異なる表現方法を採用しているためです**。32 ビットの二進数を次のように表します。
$$
b_{31} b_{30} b_{29} \ldots b_2 b_1 b_0
$$
IEEE 754標準によると、32ビットの`float`は次の3つの部分構成されます
IEEE 754 標準によれば、32-bit 長の `float` は次の 3 つの部分から構成されます
- 符号ビット$\mathrm{S}$1ビットを占有し、$b_{31}$に対応します。
- 指数ビット$\mathrm{E}$8ビットを占有し、$b_{30} b_{29} \ldots b_{23}$に対応します。
- 仮数ビット$\mathrm{N}$23ビットを占有し、$b_{22} b_{21} \ldots b_0$に対応します。
- 符号$\mathrm{S}$ 1 ビットを占、$b_{31}$ に対応します。
- 指数$\mathrm{E}$ 8 ビットを占、$b_{30} b_{29} \ldots b_{23}$ に対応します。
- 仮数$\mathrm{N}$ 23 ビットを占、$b_{22} b_{21} \ldots b_0$ に対応します。
二進`float`数の値は次のように計算されます
二進`float` に対応する値は次式で計算されます
$$
\text{val} = (-1)^{b_{31}} \times 2^{\left(b_{30} b_{29} \ldots b_{23}\right)_2 - 127} \times \left(1 . b_{22} b_{21} \ldots b_0\right)_2
\text {val} = (-1)^{b_{31}} \times 2^{\left(b_{30} b_{29} \ldots b_{23}\right)_2-127} \times\left(1 . b_{22} b_{21} \ldots b_0\right)_2
$$
十進公式に変換すると、次のようになります
十進数に直すと、計算式は次のようになります
$$
\text{val} = (-1)^{\mathrm{S}} \times 2^{\mathrm{E} - 127} \times (1 + \mathrm{N})
\text {val}=(-1)^{\mathrm{S}} \times 2^{\mathrm{E} -127} \times (1 + \mathrm{N})
$$
成分の範囲は:
項の取り得る範囲は次のとおりです。
$$
\begin{aligned}
\mathrm{S} \in & \{ 0, 1\}, \quad \mathrm{E} \in \{ 1, 2, \dots, 254 \} \newline
(1 + \mathrm{N}) = & (1 + \sum_{i=1}^{23} b_{23-i} \times 2^{-i}) \subset [1, 2 - 2^{-23}]
(1 + \mathrm{N}) = & (1 + \sum_{i=1}^{23} b_{23-i} 2^{-i}) \subset [1, 2 - 2^{-23}]
\end{aligned}
$$
![IEEE 754標準での浮動小数点数の計算例](number_encoding.assets/ieee_754_float.png)
![IEEE 754 標準における float の計算例](number_encoding.assets/ieee_754_float.png)
図を観察すると、例のデータ$\mathrm{S} = 0$$\mathrm{E} = 124$、$\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$が与えられた場合
上図をると、例として $\mathrm{S} = 0$$\mathrm{E} = 124$ 、$\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$ が与えられた場合、次のようになります。
$$
\text{val} = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875
\text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875
$$
これで最初の問に答えることができます**`float`の表現には指数ビットが含まれているため、`int`よりはるかに大きい範囲を持ちます**。上の計算に基づくと`float`で表現可能な最大正の数は$2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$で、最小負の数は符号ビットを切り替えることで得られます。
これで最初の問に答えられます**`float` の表現方法には指数が含まれているため、その値域は `int` よりはるかに広い**のです。上の計算より`float` が表せる最大の正数は $2^{254 - 127} \times (2 - 2^{-23}) \approx 3.4 \times 10^{38}$ であり、符号ビットを切り替えれば最小の負数が得られます。
**しかし、`float`の拡張された範囲のトレードオフは精度犠牲**。整数型`int`32ビットすべてを数値表現に使用し、値は均等に分布していますが、指数ビットのため、`float`値が大きほど、隣接する数値間の差大きくなります。
**浮動小数点数 `float` は値域を広げる一方で、その代償として精度犠牲にしていま**。整数型 `int`32 ビットすべてを数値表現に使うため、数値は一様に分布します。しかし指数部があるため、浮動小数点数 `float`値が大きくなるほど、隣り合う 2 つの数の差大きくなる傾向があります。
以下の表に示すように、指数ビット$\mathrm{E} = 0$$\mathrm{E} = 255$は特別な意味を持ち、**0、無限大、$\mathrm{NaN}$などを表現するために使用されます**。
次の表のとおり、指数部 $\mathrm{E} = 0$$\mathrm{E} = 255$は特別な意味があり、**ゼロ、無限大、$\mathrm{NaN}$ などを表ために使れます**。
<p align="center"> 表 <id> &nbsp; 指数ビットの意味 </p>
<p align="center"> 表 <id> &nbsp; 指数の意味 </p>
| 指数ビットE | 仮数ビット$\mathrm{N} = 0$ | 仮数ビット$\mathrm{N} \ne 0$ | 計算式 |
| ------------------ | ----------------------------- | ------------------------------- | ---------------------------------------------------------------------- |
| $0$ | $\pm 0$ | 非正規化数 | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ |
| $1, 2, \dots, 254$ | 正規化数 | 正規化数 | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ |
| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | |
| 指数部 E | 仮数$\mathrm{N} = 0$ | 仮数$\mathrm{N} \ne 0$ | 計算式 |
| ------------------ | ----------------------- | ------------------------- | ---------------------------------------------------------------------- |
| $0$ | $\pm 0$ | 非正規化数 | $(-1)^{\mathrm{S}} \times 2^{-126} \times (0.\mathrm{N})$ |
| $1, 2, \dots, 254$ | 正規化数 | 正規化数 | $(-1)^{\mathrm{S}} \times 2^{(\mathrm{E} -127)} \times (1.\mathrm{N})$ |
| $255$ | $\pm \infty$ | $\mathrm{NaN}$ | |
非正規化数浮動小数点数の精度を大幅に向上させることは注目に値します。最小の正の正規化数は$2^{-126}$、最小の正の非正規化数は$2^{-126} \times 2^{-23}$です。
なお、非正規化数によって浮動小数点数の精度は大きく向上します。最小の正の正規化数は $2^{-126}$ であり、最小の正の非正規化数は $2^{-126} \times 2^{-23}$ です。
倍精度`double``float`と同様の表現方法を使用しますが、簡潔さのためここでは詳述しません。
倍精度 `double``float` と同様の表現方法を用しているためここでは詳述しません。
+31 -31
View File
@@ -1,66 +1,66 @@
# まとめ
### 重要ポイント
### 重要ポイントの振り返り
- データ構造は論理構造と物理構造の2つの観点から分類できます。論理構造はデータ間の論理的関係を記述し、物理構造はデータがメモリにどのように格納されるかを記述します。
- よく使用される論理構造には、線形構造、木、ネットワークがあります。通常、論理構造に基づいてデータ構造を線形(配列、連結リスト、スタック、キュー)と非線形(木、グラフ、ヒープ)に分けます。ハッシュ表の実装は線形と非線形の両方のデータ構造を含む場合があります。
- プログラム実行中の際、データはメモリに格納されます。各メモリ空間には対応するアドレスがあり、プログラムはれらのアドレスを通じてデータにアクセスします。
- 物理構造は連続空間格納(配列)と離散空間格納(連結リスト)に分けることができます。すべてのデータ構造は配列、連結リスト、またはその両方の組み合わせを使用して実装されます。
- コンピュータ基本データ型には、整数`byte``short``int``long`、浮動小数点数`float``double`、文字`char`)、ブール値(`bool`)が含まれます。データ型の値の範囲は、そのサイズと表現に依存します。
- 符号絶対値、1の補数、2の補数は、コンピュータで整数をエンコードする3つの方法であり、相互に変換することができます。符号絶対値の最上位ビット符号ビットで、残りのビット数値の値を表します。
- 整数はコンピュータで2の補数によってエンコードされます。この表現の利点には、(i)コンピュータが正と負の整数の加算を統一できる、(ii)減算用の特別なハードウェア回路を設計する必要がない、(iii)正と負の0の曖昧さがない、がありま
- 浮動小数点数のエンコーディングは、1つの符号ビット、8つの指数ビット、23の仮数ビットで構成されます。指数ビットのため、浮動小数点数の範囲は整数よりはるかに大きくなりますが、精度犠牲にます。
- ASCIIは最初期の英語文字セットで、1バイトの長さで計127文字です。GBKは人気のある中国語文字セットで、2万字以上の中国語文字を含みます。Unicodeは世界の様々な言語の文字を含む完全な文字セット標準を提供することを目的とし、文字エンコーディング方法の不一致による文字化け問題を解決します。
- UTF-8は最も人気があり一般的なUnicodeエンコーディング方法です。これは可変長エンコーディング方法で、優れた拡張性と空間効率を持ちます。UTF-16UTF-32は固定長エンコーディング方法です。中国語文字をエンコードする際、UTF-16UTF-8よりも少ない空間を使用します。JavaやC#などのプログラミング言語はデフォルトでUTF-16エンコーディングを使用します。
- データ構造は論理構造と物理構造という 2 つの観点から分類できます。論理構造はデータ要素間の論理的関係を記述し、物理構造はデータのコンピュータメモリ上での格納方法を記述します。
- 代表的な論理構造には、線形、木構造、網状構造などがあります。通常、論理構造に基づいてデータ構造を線形(配列、連結リスト、スタック、キュー)と非線形(木、グラフ、ヒープ)の 2 種類に分類します。ハッシュテーブルの実装には、線形データ構造と非線形データ構造が同時に含まれる場合があります。
- プログラム実行、データはコンピュータメモリに格納されます。各メモリ空間には対応するメモリアドレスがあり、プログラムはれらのメモリアドレスを通じてデータにアクセスします。
- 物理構造は主に連続領域への格納(配列)と分散領域への格納(連結リスト)に分けられます。すべてのデータ構造は配列、連結リスト、またはその両方の組み合わせによって実装されます。
- コンピュータにおける基本データ型には、整数 `byte``short``int``long`、浮動小数点数 `float``double`、文字 `char`、真偽値 `bool` があります。これらの値域は、使用する記憶領域の大きさと表現方式によって決まります。
- 符号付き絶対値表現、1 の補数、2 の補数は、コンピュータで数値を符号化する 3 つの方法であり、相互に変換できます。整数の符号付き絶対値表現では最上位ビット符号ビットで、残りのビット数値の値す。
- 整数はコンピュータ内では 2 の補数の形式で格納されます。2 の補数表現では、コンピュータは正数と負数の加算を同じように扱うことができ、減算のために特別なハードウェア回路を別途設計する必要がなく、さらに正負のゼロが重複する問題もありません
- 浮動小数点数の符号化は、1 ビットの符号部、8 ビットの指数部、23 ビットの仮数部で構成されます。指数部があるため、浮動小数点数の値域は整数よりはるかにくなりますが、その代償として精度犠牲になります。
- ASCII コードは最も早く登場した英字文字集合で、長さは 1 バイト、収録文字数は 127 です。GBK 文字集合はよく使われる中国語文字集合で、2 万字以上の漢字を収録しています。Unicode は完全な文字集合標準を提供することを目指しており、世界中のさまざまな言語の文字を収録することで、文字コード方式の不一致によって生じる文字化け問題を解決します。
- UTF-8 は最も広く使われている Unicode の符号化方式で、汎用性が非常に高いです。可変長の符号化方式であり、拡張性に優れ、記憶領域の利用効率を効果的に高めます。UTF-16UTF-32 は固定長の符号化方式です。中国語を符号化する場合、UTF-16UTF-8 よりも使用領域が小さくなります。Java や C# などのプログラミング言語はデフォルトで UTF-16 を使用します。
### Q & A
**Q**: なぜハッシュ表は線形と非線形の両方のデータ構造を含むのですか?
**Q**なぜハッシュテーブルには線形データ構造と非線形データ構造が同時に含まれるのですか?
ハッシュ表の基礎構造は配列です。ハッシュ衝突を解決するために「チェイン法」を使用する場合があります(後の節「ハッシュ衝突」で説明):配列の各バケットは連結リストを指し、その長さが特定の閾値より大きくなると木(通常は赤黒木)に変換される可能性があります。
格納の観点から、ハッシュ表の基礎構造は配列で、各バケットには値、連結リスト、または木が含まれる場合があります。したがって、ハッシュ表は線形データ構造(配列、連結リスト)と非線形データ構造(木)の両方を含む場合があります。
ハッシュテーブルの基盤は配列であり、ハッシュ衝突を解決するために「チェイン法」(後続の「ハッシュ衝突」の章で説明します)を使うことがあります。配列の各バケットは 1 つの連結リストを指し、その連結リストの長さがある閾値を超えると木(通常は赤黒木)に変換されることもあります。
**Q**: `char`型の長さは1バイトですか?
格納の観点から見ると、ハッシュテーブルの基盤は配列であり、各バケットスロットには値が入ることもあれば、連結リストや木が入ることもあります。したがって、ハッシュテーブルには線形データ構造(配列、連結リスト)と非線形データ構造(木)が同時に含まれる場合があります。
`char`型の長さは、プログラミング言語のエンコーディング方法によって決まります。例えば、Java、JavaScript、TypeScript、C#はすべてUTF-16エンコーディング(Unicodeコードポイントを保存するため)を使用するため、`char`型の長さは2バイトです
**Q**`char` 型の長さは 1 バイトですか?
**Q**: 配列ベースのデータ構造を「静的データ構造」と呼ぶことに曖昧さはありませんか?スタックもプッシュやポップなどの「動的」操作を実行できます。
`char` 型の長さは、プログラミング言語が採用する符号化方式によって決まります。たとえば、Java、JavaScript、TypeScript、C# はいずれも UTF-16 符号化(Unicode コードポイントを保持)を採用しているため、`char` 型の長さは 2 バイトです。
スタックは動的なデータ操作を実装できますが、データ構造は依然として「静的」です(長さが固定)。配列ベースのデータ構造は動的に要素を追加または削除できますが、その容量は固定されています。スタックサイズが事前に割り当てられたサイズを超える場合、古い配列は新しく作成されたより大きな配列にコピーされます。
**Q**:配列ベースで実装されたデータ構造を「静的データ構造」と呼ぶのは曖昧ではありませんか? スタックも push や pop などの操作ができ、これらの操作はどれも「動的」です。
**Q**: スタック(キュー)を構築する際、そのサイズが指定されていないのに、なぜ「静的データ構造」なのですか?
スタックは確かに動的なデータ操作を実現できますが、データ構造自体は依然として「静的」(長さが不変)です。配列ベースのデータ構造でも要素を動的に追加または削除できますが、その容量は固定です。データ量が事前に確保した大きさを超えた場合は、より大きな新しい配列を作成し、古い配列の内容を新しい配列にコピーする必要があります。
高級プログラミング言語では、スタック(キュー)の初期容量を手動で指定する必要はありません。このタスクはクラス内で自動的に完了されます。例えば、Javaの`ArrayList`の初期容量は通常10です。さらに、拡張操作も自動的に完了されます。詳細については、後続の「リスト」の章を参照してください。
**Q**:スタック(キュー)を構築するときにサイズを指定していないのに、なぜそれらは「静的データ構造」なのですか?
**Q**: 符号絶対値を2の補数に変換する方法は「最初に否定してから1を加える」ですので、2の補数を符号絶対値に変換することはその逆操作「最初に1を減算してから否定する」であるべきです
しかし、2の補数も「最初に否定してから1を加える」を通じて符号絶対値に変換できます。なぜですか?
高水準プログラミング言語では、スタック(キュー)の初期容量を人手で指定する必要はなく、この作業はクラス内部で自動的に行われます。たとえば、Java の `ArrayList` の初期容量は通常 10 です。また、容量拡張も自動的に実装されています。詳しくは後続の「リスト」の章を参照してください
**A**: これは、符号絶対値と2の補数間の相互変換が「補数」の計算と等価だからです。まず補数を定義します:$a + b = c$と仮定すると、$a$は$b$の$c$に対する補数と言い、逆に$b$は$a$の$c$に対する補数と言います。
**Q**:符号付き絶対値表現から 2 の補数への変換方法は「先にビット反転してから 1 を加える」ですが、2 の補数から符号付き絶対値表現への変換は逆演算である「先に 1 を引いてからビット反転する」べきなのに、同じく「先にビット反転してから 1 を加える」でも求められます。これはなぜですか?
長さ$n = 4$の二進数$0010$が与えられた場合、この数が符号絶対値(符号ビットを無視)の場合、その2の補数は「最初に否定してから1を加える」ことで得られます
これは、符号付き絶対値表現と 2 の補数の相互変換が、実際には「補数」を計算する過程だからです。まず補数の定義を示します。$a + b = c$ とすると、$a$ を $b$ から $c$ への補数と呼び、逆に $b$ も $a$ から $c$ への補数と呼びます
長さ $n = 4$ ビットの 2 進数 $0010$ が与えられたとします。この数を符号付き絶対値表現(符号ビットは考慮しない)とみなすと、その 2 の補数は「先にビット反転してから 1 を加える」ことで得られます。
$$
0010 \rightarrow 1101 \rightarrow 1110
$$
符号絶対値と2の補数の和$0010 + 1110 = 10000$であることを観察します。つまり、2の補数$1110$は符号絶対値$0010$$10000$に対する「補数」です。**これは、上記の「最初に否定してから1を加える」が$10000$に対する補数計算と等価であることを意味します**
ここで、符号付き絶対値表現と 2 の補数の和$0010 + 1110 = 10000$ となります。つまり、2 の補数 $1110$ は符号付き絶対値表現 $0010$ から $10000$ への「補数」です。**これは、上記の「先にビット反転してから 1 を加える」が、実際には $10000$ への補数計算する過程であることを意味します**
では、$1110$$10000$に対する「補数」はでしょうか?「最初に否定してから1を加える」ことで計算できます
では、2 の補数 $1110$ から $10000$ への「補数」はいくつでしょうか。これもやはり「先にビット反転してから 1 を加える」ことで求められます
$$
1110 \rightarrow 0001 \rightarrow 0010
$$
言い換えると、符号絶対値と2の補数は互いに$10000$に対する「補数」であるため、「符号絶対値から2の補数」と「2の補数から符号絶対値」は同じ操作(最初に否定してから1を加える)で実できます。
言い換えると、符号付き絶対値表現と 2 の補数は互いに相手から $10000$ への「補数」なので、「符号付き絶対値表現から 2 の補数への変換」と「2 の補数から符号付き絶対値表現への変換」は同じ操作(先にビット反転してから 1 を加える)で実できます。
もちろん、「最初に否定してから1を加える」の逆操作を使用して2の補数$1110$の符号絶対値を求めることもできます。つまり、「最初に1を減算してから否定する」:
もちろん、逆演算を用いて 2 の補数 $1110$ の符号付き絶対値表現を求めることもでき、その場合は「先に 1 を引いてからビット反転する」ことになります。
$$
1110 \rightarrow 1101 \rightarrow 0010
$$
要約すると、「最初に否定してから1を加える」と「最初に1を減算してから否定する」は両方とも$10000$に対する補数を計算しており、等価です。
まとめると、「先にビット反転してから 1 を加える」と「先に 1 を引いてからビット反転する」の 2 つの演算は、どちらも $10000$ への補数を計算しており、等価です。
本質的に、「否定」操作は実際には$1111$に対する補数を求めることです(`符号絶対値 + 1の補数 = 1111`常に成り立つため)。そして1の補数に1を加えることは$10000$に対する2の補数と等しくなります。
本質的に、「ビット反転」という操作は実際には $1111$ への補数を求めています(常に `符号付き絶対値表現 + 1 の補数 = 1111` が成り立つため)。そして、1 の補数にさらに 1 を加えて得られる 2 の補数が、$10000$ への補数です。
上記では$n = 4$を例に取りましたが、任意の桁数の任意の二進数に一般化できます。
上記では $n = 4$ を例にましたが、この考え方は任意のビット長の 2 進数に一般化できます。