用一个有趣的问题帮助理解计算机思维

    (本文完整的pdf版请关注公众号 “张张学算法” 并回复 “221026” 自行领取~)

    固定一个具有固定变长的黑白相间的板子位置不动,取一个半径已知的圆在板子上以固定步进水平移动,计算每个时刻圆内黑白颜色面积之比。

思路一:数学思维

    首先想到的是数学思路,每个时刻的圆函数已知,板子是由若干条横竖线构成,那么可以通过每个时刻圆和多条横竖线求交点来计算面积之比。显然这种想法最容易,但也很难实现,因为板子的横竖线难以用一个固定的函数关系表示出来。

思路二:计算机思维

    计算机可以很容易解决数学难以求解的问题,因此我想到了蒙特卡洛的思想 —— 首先对板子进行离散化,用点的个数之比近似面积之比。板子被等分的程度越大,个数之比越接近于面积之比。下面重点介绍一下蒙特卡洛思想的解题思路。

    第一步:参数初始化。初始化格子边长、圆的半径、圆的步进、圆心初始位置;

    第二步:准备工作。生成一个1, -1相间的矩阵,用以代表棋盘格,并将板子离散化 —— 将每个格子等分成n*n份;(假设棋盘格无限大,即圆水平移动的范围不超出棋盘格边界)

    第三步:作圆滚动的动态图。根据圆心初始位置和步进,计算每个时刻圆心坐标,并已知圆的半径,可以根据极坐标转直角坐标计算出圆上每个离散点的坐标,将离散点连线即圆;

    第四步:筛选圆内点。首先筛选出一个小区域,该小区域要完全包含圆,对该小区域内的每个点判断是否在圆内,若点到圆心的距离小于半径则该点在圆内;

    第五步:计算面积比。计算圆内每个颜色点的个数,即逐个判断圆内点是1还是-1,统计个数并计算比值,即为近似的面积比。

    第六步:作图。作出和圆水平移动同步的面积比曲线图。

%%
clear; clc; close all; warning off;
rng(1);
set(0,'defaultAxesFontName', 'Monospaced');  % 防止中文乱码

%% 参数设置
Circle = 6;  
n = 20;  % 每个格子被20*20等分
l = 0.09;  % 格子边长(m)
d = 0.0175;  % 步进
r = 0.0358;  % 半径
pos0 = [l*rand(1), 9*l*rand(1)];  % 初始圆心坐标


%% 生成棋盘格
data_per_cell = [-ones(n, n), ones(n, n); ones(n, n), -ones(n, n)];
Data = repmat(data_per_cell, Circle);

% 作图
figure(1);
annotation('textbox', [0.1, 0.85, 0.1, 0.1], 'LineStyle', '-', 'LineWidth', 1.5, 'String', ['起始圆心坐标为:(', num2str(pos0(1), '%.2f'), ', ', num2str(pos0(2), '%.2f'), ')']);

subplot(1, 2, 1);
N = n * Circle * 2;
axis_limit = [-l, l*(Circle-1)*2];
axis_lis = linspace(axis_limit(1), axis_limit(2), N);
pcolor(axis_lis, axis_lis, Data); colorbar; hold on;
xlabel('X/m'); ylabel('Y/m'); 
axis('tight'); axis('equal');


%% 光斑滚动
beta = 0 : pi/50 : 2*pi;
xcenter = pos0(1) : d : l*((Circle-2)*2+1);
h = [];
ycenter = pos0(2);
AreaRatio = [];
for i = 1 : length(xcenter)
    x = r .* cos(beta) + xcenter(i);
    y = r .* sin(beta) + ycenter;
    
    x_idx_lis = find(axis_lis>xcenter(i)-l & axis_lis<xcenter(i)+l);
    y_idx_lis = find(axis_lis>ycenter-l & axis_lis<ycenter+l);
    
    center = [xcenter(i), ycenter];
    cnt_white = 0; cnt_black = 0;
    for mm = 1 : length(x_idx_lis)
        for nn = 1 : length(y_idx_lis)
            pos = [axis_lis(x_idx_lis(mm)), axis_lis(y_idx_lis(nn))];
            dis = norm(pos-center);  % 点到圆心的距离不大于半径,则认为点在圆内
            if dis <= r
                if Data(x_idx_lis(mm), y_idx_lis(nn)) == -1
                    cnt_black = cnt_black + 1;
                else
                    cnt_white = cnt_white + 1;
                end
            end
        end
    end
    
    area_ratio = cnt_white/cnt_black;
    area_ratio = min(area_ratio, 6);  
    AreaRatio(end+1) = area_ratio;    
    
    % 作图
    figure(1); subplot(1, 2, 1);
    if isa(h, 'matlab.graphics.chart.primitive.Line')
        delete(h);
    end
    h = plot(x, y, 'm', 'linewidth', 3); 
    title(['黄格个数=', num2str(cnt_white), ',  蓝格个数=', num2str(cnt_black), ',  面积比=', num2str(area_ratio, '%.2f')]);
    set(gca, 'fontsize', 14); 
    
    subplot(1, 2, 2); grid on;
    plot([1, length(xcenter)+1], [1, 1], 'm--', 'linewidth', 1.5); hold on;
    plot(1:i, AreaRatio, 'bo-', 'linewidth', 2); hold on;
    xlabel('X'); ylabel('面积比'); 
    title(['圆的步进d=', num2str(d*100, '%.2f'), 'cm,  圆半径r=', num2str(r*100, '%.2f'), 'cm']);
    axis('tight'); axis('square'); ylim([-0.2, 6.2]); set(gca, 'fontsize', 14); 
    set(gcf, 'position', [12, 60, 1450, 650]);
    
    MakeGif('circle-moves.gif', i);
end
    

function MakeGif(filename, index)  
    f = getframe(gcf);  
    imind = frame2im(f);  
    [imind,cm] = rgb2ind(imind, 256);  
    if index == 1  
        imwrite(imind, cm, filename, 'gif', 'Loopcount', inf, 'DelayTime', 0.1);
    else  
        imwrite(imind, cm, filename, 'gif', 'WriteMode', 'append', 'DelayTime', 0.1);
    end  
end

运行结果:

circle-moves-horizontally

猜你喜欢

转载自blog.csdn.net/weixin_40583722/article/details/127541253