【Goで学ぶコンピューターサイエンス】データ圧縮の基本

## データ圧縮

データ圧縮とは、データのサイズを小さくする技術です。圧縮されたデータは復元可能な場合（可逆圧縮）もあれば、一部の情報が失われる場合（非可逆圧縮）もあります。

## データ圧縮の種類

### 可逆圧縮
圧縮前のデータを完全に復元可能な方法です。正確さが求められる場合に使用されます。

- ZIP
- PNG
- GIF

### 非可逆圧縮
データの一部を削除して圧縮する方法です。人間が気づきにくい情報を削除することでサイズを大幅に削減します。

- JPEG
- MP3
- H.264

## 基本的な圧縮アルゴリズム

### ハフマン符号化
ハフマン符号化は、データ内の文字の頻度に基づいてビット列を割り当てる方法です。頻繁に出現する文字ほど短いビット列が割り当てられるため、全体のデータ量を削減できます。

### ランレングス符号化（RLE）
RLEは、連続する同じ値をカウントし、圧縮します。例えば、「AAAAABBBCCCD」は「5A3B3C1D」に変換されます。

### LZ77（Lempel-Ziv 1977）
データの重複部分を検出し、参照として保存する手法です。ZIPやgzipで使用されます。

## 可逆圧縮のGoサンプルコード

```go
package main

import (
	"bytes"
	"compress/gzip"
	"fmt"
	"io"
)

func compress(data []byte) ([]byte, error) {
	var buf bytes.Buffer
	gz := gzip.NewWriter(&buf)
	_, err := gz.Write(data)
	if err != nil {
		return nil, err
	}
	gz.Close()
	return buf.Bytes(), nil
}

func decompress(data []byte) ([]byte, error) {
	buf := bytes.NewReader(data)
	gz, err := gzip.NewReader(buf)
	if err != nil {
		return nil, err
	}
	defer gz.Close()

	var result bytes.Buffer
	_, err = io.Copy(&result, gz)
	if err != nil {
		return nil, err
	}
	return result.Bytes(), nil
}

func main() {
	original := []byte("Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.Go is an amazing programming language! Go is efficient and fast.")
	fmt.Println("Original Data Size:", len(original))

	// 圧縮
	compressed, err := compress(original)
	if err != nil {
		fmt.Println("Compression error:", err)
		return
	}
	fmt.Println("Compressed Data Size:", len(compressed))

	// 解凍
	decompressed, err := decompress(compressed)
	if err != nil {
		fmt.Println("Decompression error:", err)
		return
	}
	fmt.Println("Decompressed Data Size:", len(decompressed))
}
```
```
Original Data Size: 1600
Compressed Data Size: 95
Decompressed Data Size: 1600
```

## 非可逆圧縮のGoサンプルコード

```go
package main

import (
	"fmt"
	"image"
	"image/jpeg"
	"os"
)

func compressJPEG(inputPath, outputPath string, quality int) error {
	// 入力画像を読み込む
	inputFile, err := os.Open(inputPath)
	if err != nil {
		return err
	}
	defer inputFile.Close()

	img, _, err := image.Decode(inputFile)
	if err != nil {
		return err
	}

	// 出力ファイルを作成
	outputFile, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer outputFile.Close()

	// JPEGとして保存（品質を指定）
	options := &jpeg.Options{Quality: quality}
	err = jpeg.Encode(outputFile, img, options)
	if err != nil {
		return err
	}

	return nil
}

func main() {
	inputPath := "input.jpg"   // 入力画像のパス
	outputPath := "output.jpg" // 圧縮後画像の保存先
	quality := 10              // 圧縮品質（1-100, 数値が低いほど圧縮率が高い）

	err := compressJPEG(inputPath, outputPath, quality)
	if err != nil {
		panic(err)
	}

	fmt.Println("圧縮が完了しました。")
}
```