《Go语言圣经/gopl》第三章习题

目录

github

3.1-3.4 

前置知识:

官方示例代码:

如何使用这段代码?

3.1

题目

解析

全代码

3.2

题目

解析

全代码(以生成eggBox为例)

3.3

题目

解析

全代码:

3.4

题目:

解析

全代码

3.5-3.9

前置知识 

官方示例代码

为什么看不到生成的png图片?

3.5

题目

解析

全代码:

3.6

题目:

解析:

全代码

3.7

题目

解析

全代码

3.8

题目

解析

全代码

3.9

题目

解析

全代码

3.10-3.12

前置知识

官网代码

3.10-3.11

题目

解析

全代码

3.12

题目

解析

全代码

3.13

3.13

题目

解析

全代码


3.1-3.4

前置知识:

 官方示例代码:

// Surface computes an SVG rendering of a 3-D surface function.
package main

import (
    "fmt"
    "math"
)

const (
    width, height = 600, 320            // canvas size in pixels
    cells         = 100                 // number of grid cells
    xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)
    xyscale       = width / 2 / xyrange // pixels per x or y unit
    zscale        = height * 0.4        // pixels per z unit
    angle         = math.Pi / 6         // angle of x, y axes (=30°)
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
    fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
        "style='stroke: grey; fill: white; stroke-width: 0.7' "+
        "width='%d' height='%d'>", width, height)
    for i := 0; i < cells; i++ {
        for j := 0; j < cells; j++ {
            ax, ay := corner(i+1, j)
            bx, by := corner(i, j)
            cx, cy := corner(i, j+1)
            dx, dy := corner(i+1, j+1)
            fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
                ax, ay, bx, by, cx, cy, dx, dy)
        }
    }
    fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64) {
    // Find point (x,y) at corner of cell (i,j).
    x := xyrange * (float64(i)/cells - 0.5)
    y := xyrange * (float64(j)/cells - 0.5)

    // Compute surface height z.
    z := f(x, y)

    // Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
    sx := width/2 + (x-y)*cos30*xyscale
    sy := height/2 + (x+y)*sin30*xyscale - z*zscale
    return sx, sy
}

func f(x, y float64) float64 {
    r := math.Hypot(x, y) // distance from (0,0)
    return math.Sin(r) / r
}

如何使用这段代码?

我们可以注意到该代码生成的是svg格式文件,所以我们要将输出输入到一个svg文件中。

注意:应该视本地环境情况更改文件名字

go run ./surface.go > out.svg

运行命令后应该会在当前文件夹生成一个.svg文件,此时我们需要将svg文件转换成png/jpg文件,这个步骤可以使用这个网址(可能需要科技),或者自行搜索解决。

转换后我们便可以得到书中示例图片

3.1

题目

如果f函数返回的是无限制的float64值,那么SVG文件可能输出无效的多边形元素(虽然许多SVG渲染器会妥善处理这类问题)。修改程序跳过无效的多边形。

解析

这里我们只需要使用golang中Math包的两个函数:Math.IsInf()Math.IsNaN(),即可对一个float64值进行判断是否无限制,这里作者选择将这两个函数写到一起

// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
	if math.IsInf(x, 0) || math.IsNaN(x) {
		return true
	}
	return false
}

随后我们仅需在f函数中和main函数中修改即可

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // distance from (0,0)

	if isInfinite(r) {
		return 0
	}

	return math.Sin(r) / r
}

func main() {
	fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; fill: white; stroke-width: 0.7' "+
		"width='%d' height='%d'>", width, height)
	for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay := corner(i+1, j)
			bx, by := corner(i, j)
			cx, cy := corner(i, j+1)
			dx, dy := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
				ax, ay, bx, by, cx, cy, dx, dy)
		}
	}
	fmt.Println("</svg>")
}

全代码

// Surface computes an SVG rendering of a 3-D surface function.
// go run ./surface.go > test.svg
package main

import (
	"fmt"
	"math"
)

const (
	width, height = 600, 320            // canvas size in pixels
	cells         = 100                 // number of grid cells
	xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)
	xyscale       = width / 2 / xyrange // pixels per x or y unit
	zscale        = height * 0.4        // pixels per z unit
	angle         = math.Pi / 6         // angle of x, y axes (=30°)
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
	fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; fill: white; stroke-width: 0.7' "+
		"width='%d' height='%d'>", width, height)
	for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay := corner(i+1, j)
			bx, by := corner(i, j)
			cx, cy := corner(i, j+1)
			dx, dy := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
				ax, ay, bx, by, cx, cy, dx, dy)
		}
	}
	fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := f(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := width/2 + (x-y)*cos30*xyscale
	sy := height/2 + (x+y)*sin30*xyscale - z*zscale
	return sx, sy
}

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // distance from (0,0)

	if isInfinite(r) {
		return 0
	}

	return math.Sin(r) / r
}

// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
	if math.IsInf(x, 0) || math.IsNaN(x) {
		return true
	}
	return false
}

3.2

题目

试验math包中其他函数的渲染图形。你是否能输出一个egg box、moguls或a saddle图案?

解析

三种生成函数如下

func eggBox(x, y float64) float64 {
	return (math.Sin(x) + math.Sin(y)) / 10
}

func saddle(x, y float64) float64 {
	return math.Pow(y/xyrange*2, 2) - math.Pow(x/xyrange*2, 2)
}

func moguls(x, y float64) float64 {
	return math.Pow(math.Sin(x/xyrange*3*math.Pi), 2) * math.Cos(y/xyrange*3*math.Pi)
}

将其替换corner函数中的生成z轴即可,如生成moguls图案:

func corner(i, j int) (float64, float64) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := moguls(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := width/2 + (x-y)*cos30*xyscale
	sy := height/2 + (x+y)*sin30*xyscale - z*zscale
	return sx, sy
}

全代码(以生成eggBox为例)

// Surface computes an SVG rendering of a 3-D surface function.
// go run surface.go > test.svg
package main

import (
	"fmt"
	"math"
)

const (
	width, height = 600, 320            // canvas size in pixels
	cells         = 100                 // number of grid cells
	xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)
	xyscale       = width / 2 / xyrange // pixels per x or y unit
	zscale        = height * 0.4        // pixels per z unit
	angle         = math.Pi / 6         // angle of x, y axes (=30°)
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
	fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; fill: white; stroke-width: 0.7' "+
		"width='%d' height='%d'>", width, height)
	for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay := corner(i+1, j)
			bx, by := corner(i, j)
			cx, cy := corner(i, j+1)
			dx, dy := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
				ax, ay, bx, by, cx, cy, dx, dy)
		}
	}
	fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := eggBox(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := width/2 + (x-y)*cos30*xyscale
	sy := height/2 + (x+y)*sin30*xyscale - z*zscale
	return sx, sy
}

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // distance from (0,0)

	if isInfinite(r) {
		return 0
	}

	return math.Sin(r) / r
}

func eggBox(x, y float64) float64 {
	return (math.Sin(x) + math.Sin(y)) / 10
}

func saddle(x, y float64) float64 {
	return math.Pow(y/xyrange*2, 2) - math.Pow(x/xyrange*2, 2)
}

func moguls(x, y float64) float64 {
	return math.Pow(math.Sin(x/xyrange*3*math.Pi), 2) * math.Cos(y/xyrange*3*math.Pi)
}

// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
	if math.IsInf(x, 0) || math.IsNaN(x) {
		return true
	}
	return false
}

3.3

题目

根据高度给每个多边形上色,那样峰值部将是红色(#ff0000),谷部将是蓝色(#0000ff)。

解析

给多边形上色仅需将生成函数更改成以下格式即可

fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)

其中峰值和谷部可以根据当前z轴来仅需确定,为了方便此处将z轴<0的设置为谷部,>0的设置为峰部

func corner(i, j int) (float64, float64, bool) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := f(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := width/2 + (x-y)*cos30*xyscale
	sy := height/2 + (x+y)*sin30*xyscale - z*zscale
    //确定峰还是谷
	return sx, sy, z >= 0
}

//新的生成函数
for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay, _ := corner(i+1, j)
			bx, by, _ := corner(i, j)
			cx, cy, _ := corner(i, j+1)
			dx, dy, isPeak := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			color := blue
			if isPeak {
				color = red
			}

			fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
		}
	}

全代码:

// Surface computes an SVG rendering of a 3-D surface function.
// go run ./surface.go > test.svg
package main

import (
	"fmt"
	"math"
)

const (
	width, height = 600, 320            // canvas size in pixels
	cells         = 100                 // number of grid cells
	xyrange       = 30.0                // axis ranges (-xyrange..+xyrange)
	xyscale       = width / 2 / xyrange // pixels per x or y unit
	zscale        = height * 0.4        // pixels per z unit
	angle         = math.Pi / 6         // angle of x, y axes (=30°)
	red           = "#ff0000"
	blue          = "#0000ff"
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func main() {
	fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; fill: white; stroke-width: 0.7' "+
		"width='%d' height='%d'>", width, height)
	for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay, _ := corner(i+1, j)
			bx, by, _ := corner(i, j)
			cx, cy, _ := corner(i, j+1)
			dx, dy, isPeak := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			color := blue
			if isPeak {
				color = red
			}

			fmt.Printf("<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
		}
	}
	fmt.Println("</svg>")
}

func corner(i, j int) (float64, float64, bool) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := f(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := width/2 + (x-y)*cos30*xyscale
	sy := height/2 + (x+y)*sin30*xyscale - z*zscale
	return sx, sy, z >= 0
}

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // distance from (0,0)

	if isInfinite(r) {
		return 0
	}

	return math.Sin(r) / r
}

// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
	if math.IsInf(x, 0) || math.IsNaN(x) {
		return true
	}
	return false
}

3.4

题目:

 参考1.7节Lissajous例子的函数,构造一个web服务器,用于计算函数曲面然后返回SVG数据给客户端。服务器必须设置Content-Type头部:

w.Header().Set("Content-Type", "image/svg+xml")

(这一步在Lissajous例子中不是必须的,因为服务器使用标准的PNG图像格式,可以根据前面的512个字节自动输出对应的头部。)允许客户端通过HTTP请求参数设置高度、宽度和颜色等参数。

解析

 根据ch1/server1进行修改即可web获取高度和宽度:

if err := r.ParseForm(); err != nil {
		log.Fatal(err)
	}

	if r.Form["width"] != nil {
		width, _ = strconv.Atoi(r.Form["width"][0])
	}

	if r.Form["height"] != nil {
		height, _ = strconv.Atoi(r.Form["height"][0])
	}

修改高度和宽度网址示范:http://localhost:8000/?height=800&width=800

全代码

// Server2 is a minimal "echo" and counter server.
package main

import (
	"fmt"
	"log"
	"math"
	"net/http"
	"strconv"
)

var (
	width, height = 600, 320              // canvas size in pixels
	xyscale       = width / 2 / xyrange   // pixels per x or y unit
	zscale        = float64(height) * 0.4 // pixels per z unit
)

const (
	cells   = 100         // number of grid cells
	xyrange = 30.0        // axis ranges (-xyrange..+xyrange)
	angle   = math.Pi / 6 // angle of x, y axes (=30°)
	red     = "#ff0000"
	blue    = "#0000ff"
)

var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)

func corner(i, j int) (float64, float64, bool) {
	// Find point (x,y) at corner of cell (i,j).
	x := xyrange * (float64(i)/cells - 0.5)
	y := xyrange * (float64(j)/cells - 0.5)

	// Compute surface height z.
	z := f(x, y)

	// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).

	sx := float64(width)/2 + (x-y)*cos30*float64(xyscale)
	sy := float64(height)/2 + (x+y)*sin30*float64(xyscale) - z*zscale
	return sx, sy, z >= 0
}

func f(x, y float64) float64 {
	r := math.Hypot(x, y) // distance from (0,0)
	return math.Sin(r) / r
}

// IsInf 第二个参数 > 0 判断是否正无穷, < 0 判断是否负无穷, = 0 判断是否无穷
func isInfinite(x float64) bool {
	if math.IsInf(x, 0) || math.IsNaN(x) {
		return true
	}
	return false
}

func main() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/svg+xml")

	if err := r.ParseForm(); err != nil {
		log.Fatal(err)
	}

	if r.Form["width"] != nil {
		width, _ = strconv.Atoi(r.Form["width"][0])
	}

	if r.Form["height"] != nil {
		height, _ = strconv.Atoi(r.Form["height"][0])
	}

	fmt.Fprintf(w, "<svg xmlns='http://www.w3.org/2000/svg' "+
		"style='stroke: grey; fill: white; stroke-width: 0.7' "+
		"width='%d' height='%d'>", width, height)
	for i := 0; i < cells; i++ {
		for j := 0; j < cells; j++ {
			ax, ay, _ := corner(i+1, j)
			bx, by, _ := corner(i, j)
			cx, cy, _ := corner(i, j+1)
			dx, dy, isPeak := corner(i+1, j+1)

			if isInfinite(ax) || isInfinite(ay) || isInfinite(bx) || isInfinite(by) || isInfinite(cx) || isInfinite(cy) || isInfinite(dx) || isInfinite(dy) {
				continue
			}

			color := blue
			if isPeak {
				color = red
			}

			fmt.Fprintf(w, "<polygon style='fill: %s' points='%g,%g %g,%g %g,%g %g,%g'/>\n", color, ax, ay, bx, by, cx, cy, dx, dy)
		}
	}
	fmt.Fprintln(w, "</svg>")
}

3.5-3.9

前置知识 

官方示例代码

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
    "image"
    "image/color"
    "image/png"
    "math/cmplx"
    "os"
)


func main() {
    const (
        xmin, ymin, xmax, ymax = -2, -2, +2, +2
        width, height          = 1024, 1024
    )

    img := image.NewRGBA(image.Rect(0, 0, width, height))
    for py := 0; py < height; py++ {
        y := float64(py)/height*(ymax-ymin) + ymin
        for px := 0; px < width; px++ {
            x := float64(px)/width*(xmax-xmin) + xmin
            z := complex(x, y)
            // Image point (px, py) represents complex value z.
            img.Set(px, py, mandelbrot(z))
        }
    }
    png.Encode(os.Stdout, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.Color {
    const iterations = 200
    const contrast = 15

    var v complex128
    for n := uint8(0); n < iterations; n++ {
        v = v*v + z
        if cmplx.Abs(v) > 2 {
            return color.Gray{255 - contrast*n}
        }
    }
    return color.Black
}

为什么看不到生成的png图片?

由于windows奇妙的bug,可能导致os.Stdout输出错误,使得图片生成不了,可以使用以下方法解决

create, _ := os.Create("out.png")
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py++ {
		y := float64(py)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px++ {
			x := float64(px)/width*(xmax-xmin) + xmin
			z := complex(x, y)
			// Image point (px, py) represents complex value z.
			img.Set(px, py, mandelbrot(z))
		}
	}
	png.Encode(create, img) // NOTE: ignoring errors

3.5

题目

实现一个彩色的Mandelbrot图像,使用image.NewRGBA创建图像,使用color.RGBA或color.YCbCr生成颜色。

解析

修改返回的color.Color参数即可

全代码:

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
)

func main() {
	const (
		xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height          = 1024, 1024
	)
	create, _ := os.Create("out.png")
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py++ {
		y := float64(py)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px++ {
			x := float64(px)/width*(xmax-xmin) + xmin
			z := complex(x, y)
			// Image point (px, py) represents complex value z.
			img.Set(px, py, mandelbrot(z))
		}
	}
	png.Encode(create, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.Color {
	const iterations = 200
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			return color.YCbCr{255 - contrast*n, 255 - contrast*n, 255 - contrast*n}
		}
	}
	return color.White
}

3.6

题目:

 升采样技术可以降低每个像素对计算颜色值和平均值的影响。简单的方法是将每个像素分成四个子像素,实现它。

解析:

每次对py, px进行枚举时分成四个点分别进行计算即可,注意宽和高参数也应该进行相应变化

	for py := 0; py < height; py += 2 {
		y1 := float64(py)/height*(ymax-ymin) + ymin
		y2 := float64(py+1)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px += 2 {
			x1 := float64(px)/width*(xmax-xmin) + xmin
			x2 := float64(px+1)/width*(xmax-xmin) + xmin
			z1 := complex(x1, y1)
			z2 := complex(x1, y2)
			z3 := complex(x2, y1)
			z4 := complex(x2, y2)
			// Image point (px, py) represents complex value z.
			img.Set(px/2, py/2, color.Gray{
				(mandelbrot(z1).Y + mandelbrot(z2).Y + mandelbrot(z3).Y + mandelbrot(z4).Y) / 4.0,
			})
		}
	}

全代码

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
)

func main() {
	const (
		xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height          = 2048, 2048
	)
	create, _ := os.Create("out.png")
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py += 2 {
		y1 := float64(py)/height*(ymax-ymin) + ymin
		y2 := float64(py+1)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px += 2 {
			x1 := float64(px)/width*(xmax-xmin) + xmin
			x2 := float64(px+1)/width*(xmax-xmin) + xmin
			z1 := complex(x1, y1)
			z2 := complex(x1, y2)
			z3 := complex(x2, y1)
			z4 := complex(x2, y2)
			// Image point (px, py) represents complex value z.
			img.Set(px/2, py/2, color.Gray{
				(mandelbrot(z1).Y + mandelbrot(z2).Y + mandelbrot(z3).Y + mandelbrot(z4).Y) / 4.0,
			})
		}
	}
	png.Encode(create, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.Gray {
	const iterations = 200
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			return color.Gray{255 - contrast*n}
		}
	}
	return color.Gray{0}
}

3.7

题目

另一个生成分形图像的方式是使用牛顿法来求解一个复数方程,例如$z^4-1=0$。每个起点到四个根的迭代次数对应阴影的灰度。方程根对应的点用颜色表示。

解析

牛顿法细节详情见链接,此处使用牛顿迭代法

func newton(z complex128) color.Color {
	const iterations = 37
	const contrast = 7
	//0 <= iterations * contrast <= 255

	// f(x) = z * z * z * z - 1
	for n := uint8(0); n < iterations; n++ {
		z -= (z - 1/(z*z*z)) / 4
		// f(x + 1) = f(x) - f'(x) / f(x)
		if cmplx.Abs(z*z*z*z-1) < 1e-6 {
			return color.Gray{255 - contrast*n}
		}
	}
	return color.Black
}

全代码

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
	"image"
	"image/color"
	"image/png"
	"math/cmplx"
	"os"
)

func main() {
	const (
		xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height          = 1024, 1024
	)

	create, _ := os.Create("out.png")
	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py++ {
		y := float64(py)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px++ {
			x := float64(px)/width*(xmax-xmin) + xmin
			z := complex(x, y)
			// Image point (px, py) represents complex value z.
			img.Set(px, py, newton(z))
		}
	}
	png.Encode(create, img) // NOTE: ignoring errors
}

func newton(z complex128) color.Color {
	const iterations = 37
	const contrast = 7
	//0 <= iterations * contrast <= 255

	// f(x) = z * z * z * z - 1
	for n := uint8(0); n < iterations; n++ {
		z -= (z - 1/(z*z*z)) / 4
		// f(x + 1) = f(x) - f'(x) / f(x)
		if cmplx.Abs(z*z*z*z-1) < 1e-6 {
			return color.Gray{255 - contrast*n}
		}
	}
	return color.Black
}

3.8

题目

通过提高精度来生成更多级别的分形。使用四种不同精度类型的数字实现相同的分形:complex64、complex128、big.Float和big.Rat。(后面两种类型在math/big包声明。Float是有指定限精度的浮点数;Rat是无限精度的有理数。)它们间的性能和内存使用对比如何?当渲染图可见时缩放的级别是多少?

解析

不会,此处直接使用官网代码

全代码

package main

import (
	"image/color"
	"math"
	"math/big"
	"math/cmplx"
)

func mandelbrot64(z complex128) color.Color {
	const iterations = 200
	const contrast = 15
	var v complex64
	for n := uint8(0); n < iterations; n++ {
		v = v*v + complex64(z)
		if cmplx.Abs(complex128(v)) > 2 {
			if n > 50 {
				return color.RGBA{100, 0, 0, 255}
			}
			scale := math.Log(float64(n)) / math.Log(float64(iterations))
			return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
		}
	}
	return color.Black
}

func mandelbrot128(z complex128) color.Color {
	const iterations = 200
	const contrast = 15
	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			if n > 50 {
				return color.RGBA{100, 0, 0, 255}
			}
			scale := math.Log(float64(n)) / math.Log(float64(iterations))
			return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
		}
	}
	return color.Black
}

func mandelbrotBigFloat(z complex128) color.Color {
	const iterations = 200
	const contrast = 15
	zR := (&big.Float{}).SetFloat64(real(z))
	zI := (&big.Float{}).SetFloat64(imag(z))
	var vR, vI = &big.Float{}, &big.Float{}
	for i := uint8(0); i < iterations; i++ {
		vR2, vI2 := &big.Float{}, &big.Float{}
		vR2.Mul(vR, vR).Sub(vR2, (&big.Float{}).Mul(vI, vI)).Add(vR2, zR)
		vI2.Mul(vR, vI).Mul(vI2, big.NewFloat(2)).Add(vI2, zI)
		vR, vI = vR2, vI2
		squareSum := &big.Float{}
		squareSum.Mul(vR, vR).Add(squareSum, (&big.Float{}).Mul(vI, vI))
		if squareSum.Cmp(big.NewFloat(4)) == 1 {
			if i > 50 {
				return color.RGBA{100, 0, 0, 255}
			}
			scale := math.Log(float64(i)) / math.Log(float64(iterations))
			return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
		}
	}
	return color.Black
}

func mandelbrotBigRat(z complex128) color.Color {
	const iterations = 200
	const contrast = 15
	zR := (&big.Rat{}).SetFloat64(real(z))
	zI := (&big.Rat{}).SetFloat64(imag(z))
	var vR, vI = &big.Rat{}, &big.Rat{}
	for i := uint8(0); i < iterations; i++ {
		vR2, vI2 := &big.Rat{}, &big.Rat{}
		vR2.Mul(vR, vR).Sub(vR2, (&big.Rat{}).Mul(vI, vI)).Add(vR2, zR)
		vI2.Mul(vR, vI).Mul(vI2, big.NewRat(2, 1)).Add(vI2, zI)
		vR, vI = vR2, vI2
		squareSum := &big.Rat{}
		squareSum.Mul(vR, vR).Add(squareSum, (&big.Rat{}).Mul(vI, vI))
		if squareSum.Cmp(big.NewRat(4, 1)) == 1 {
			if i > 50 {
				return color.RGBA{100, 0, 0, 255}
			}
			scale := math.Log(float64(i)) / math.Log(float64(iterations))
			return color.RGBA{0, 0, 255 - uint8(scale*255), 255}
		}
	}
	return color.Black
}

3.9

题目

编写一个web服务器,用于给客户端生成分形的图像。运行客户端通过HTTP参数指定x、y和zoom参数。

解析

与3.4题并无区别,注意变量类型转换

全代码

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
	"image"
	"image/color"
	"image/png"
	"io"
	"log"
	"math"
	"math/cmplx"
	"net/http"
	"strconv"
)

func main() {
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
	x, y, zoom := 0.0, 0.0, 0.0

	if err := r.ParseForm(); err != nil {
		log.Fatal(err)
	}

	if r.Form["x"] != nil {
		x, _ = strconv.ParseFloat(r.Form["x"][0], 64)
	}

	if r.Form["y"] != nil {
		y, _ = strconv.ParseFloat(r.Form["y"][0], 64)
	}

	if r.Form["zoom"] != nil {
		zoom, _ = strconv.ParseFloat(r.Form["zoom"][0], 64)
	}
	render(w, x, y, zoom)
}

func render(out io.Writer, x, y, zoom float64) {
	const (
		// xmin, ymin, xmax, ymax = -2, -2, +2, +2
		width, height = 1024, 1024
	)
	exp := math.Exp2(1 - zoom)
	xmin, xmax := x-exp, x+exp
	ymin, ymax := y-exp, y+exp

	img := image.NewRGBA(image.Rect(0, 0, width, height))
	for py := 0; py < height; py++ {
		y := float64(py)/height*(ymax-ymin) + ymin
		for px := 0; px < width; px++ {
			x := float64(px)/width*(xmax-xmin) + xmin
			z := complex(x, y)
			// Image point (px, py) represents complex value z.
			img.Set(px, py, mandelbrot(z))
		}
	}
	png.Encode(out, img) // NOTE: ignoring errors
}

func mandelbrot(z complex128) color.Color {
	const iterations = 200
	const contrast = 15

	var v complex128
	for n := uint8(0); n < iterations; n++ {
		v = v*v + z
		if cmplx.Abs(v) > 2 {
			return color.Gray{255 - contrast*n}
		}
	}
	return color.Black
}

3.10-3.12

前置知识

官网代码

// comma inserts commas in a non-negative decimal integer string.
func comma(s string) string {
    n := len(s)
    if n <= 3 {
        return s
    }
    return comma(s[:n-3]) + "," + s[n-3:]
}

3.10-3.11

题目

练习 3.10: 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作。

练习 3.11: 完善comma函数,以支持浮点数处理和一个可选的正负号的处理。

解析

Leetcode 练习题,注意小数点后可以不添加逗号,依据题意模拟即可

bytes.Buffer使用,请观察代码

全代码

package main

import (
	"bytes"
	"fmt"
	"strings"
)

func comma(s string) string {
	var res bytes.Buffer

	if strings.HasPrefix(s, "-") {
		res.WriteString("-")
		s = s[1:]
	}

	var last string
	if strings.Contains(s, ".") {
		last = s[strings.IndexAny(s, "."):]
		s = s[:strings.IndexAny(s, ".")]
	}

	cnt := len(s) % 3
	for i := 0; i < cnt; i++ {
		res.WriteByte(s[i])
	}

	for i, j := cnt, 0; i < len(s); i, j = i+1, (j+1)%3 {
		if cnt != 0 && j == 0 {
			res.WriteString(",")
		}
		res.WriteByte(s[i])
	}

	res.WriteString(last)

	return res.String()
}

func main() {
	var s string
	fmt.Scan(&s)

	fmt.Println(comma(s))
}

3.12

题目

编写一个函数,判断两个字符串是否是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。

解析

Leetcode练习题,使用map进行判断即可,注意需要完全相等

全代码

package main

import (
	"fmt"
)

func isSame(a, b string) bool {
	hash := make(map[rune]int)

	for _, val := range a {
		hash[val]++
	}

	for _, val := range b {
		if hash[val] <= 0 {
			return false
		}
		hash[val]--
	}

	for _, val := range hash {
		if val > 0 {
			return false
		}
	}

	return true
}

func main() {
	var a, b string
	fmt.Scan(&a, &b)

	if isSame(a, b) {
		fmt.Println("YES")
	} else {
		fmt.Println("NO")
	}
}

3.13

3.13

题目

编写KB、MB的常量声明,然后扩展到YB。

解析

语法题,模拟即可

全代码

package main

const (
	KB = 1000
	MB = KB * KB
	GB = MB * KB
	TB = GB * KB
	PB = TB * KB
	EB = PB * KB
	ZB = EB * KB
	YB = ZB * KB
)

func main() {
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值