光线追踪5- Surface normals and multiple objects

news2025/3/12 19:19:04

        首先,让我们获取一个表面法线,以便进行着色。这是一个垂直于交点处表面的向量。在我们的代码中,我们需要做一个重要的设计决定:法线向量是否为任意长度,还是将其归一化为单位长度。
        诱人的是,如果不需要法线向量具有单位长度,可以跳过涉及归一化向量的昂贵平方根操作。然而,在实践中,有三个重要观察结果。首先,如果以后需要单位长度的法线向量,那么最好一次性完成它,而不是为了每个需要单位长度的位置而一次又一次地进行“以防万一”。其次,在几个地方我们确实需要单位长度的法线向量。第三,如果你需要法线向量具有单位长度,那么通常可以通过对特定几何类的构造函数或hit()函数进行理解来有效地生成该向量。例如,球体的法线可以通过除以球体半径而完全避免使用平方根来制作单位长度。

鉴于所有这些情况,我们将采用所有法线向量都具有单位长度的策略。

对于一个球体而言,外向法线是指击中点减去中心点的方向:

在球表面上,这意味着从球中心指向您的矢量是笔直向上的。现在让我们将其加入代码中并进行着色。我们还没有任何灯光之类的东西,所以让我们用颜色映射来可视化法线。一种常见的技巧是将法线的每个分量映射到0到1的区间,并将(x,y,z)映射为(red,green,blue)。对于法线,我们需要击中点,而不仅仅是确定是否击中(这是我们目前正在计算的)。场景中只有一个球体,并且它直接位于相机前方,所以我们暂时不用担心t的负值。我们只假设最近的击中点(最小的t)就是我们想要的。通过对代码进行这些更改,我们可以计算并可视化法线n:

double hit_sphere(const point3& center, double radius, const ray& r) {
vec3 oc = r.origin() - center;    
auto a = dot(r.direction(), r.direction());    
auto b = 2.0 * dot(oc, r.direction());    
auto c = dot(oc, oc) - radius*radius;    
auto discriminant = b*b - 4*a*c;
   
if(discriminant < 0){
 return -1.0; 
} 
else { 
return (-b - sqrt(discriminant) ) / (2.0*a); 
}
}
color ray_color(const ray& r) {
Auto t = hit_sphere(point3(0,0,-1), 0.5, r); 
if(t>0.0){ 
vec3 N = unit_vector(r.at(t) - vec3(0,0,-1)); 
return 0.5*color(N.x()+1, N.y()+1, N.z()+1); 
}
    vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5*(unit_direction.y() + 1.0);
return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}

这就是生成的图片:

 


6.2  简化光线与球体相交代码

让我们重新来看一下光线和球体的函数:

double hit_sphere(const point3& center, double radius, const ray& r) {    
	vec3 oc = r.origin() - center;    
	auto a = dot(r.direction(), r.direction());    
	auto b = 2.0 * dot(oc, r.direction());    
	auto c = dot(oc, oc) - radius*radius;    
	auto discriminant = b*b - 4*a*c;    
	if (discriminant < 0) {
	     return -1.0;    
	} 
	else {
	    return (-b - sqrt(discriminant) ) / (2.0*a);
	}
}

光线和球体相交的代码(之前)

首先,回想一下,一个向量与自身的点积等于该向量的长度的平方。

其次,注意到方程中的b有一个因子为2。考虑一下如果b=2h时二次方程会发生什么变化。

我们现在可以将球体相交的代码简化为以下形式:

double hit_sphere(const point3& center, double radius, const ray& r) {    
vec3 oc = r.origin() - center;
auto a = r.direction().length_squared();
auto half_b = dot(oc, r.direction());
auto c = oc.length_squared() - radius*radius;
auto discriminant = half_b*half_b - a*c;
if (discriminant < 0) {
        return -1.0;
    } 
else {
	return (-half_b - sqrt(discriminant) ) / a;
}
}

6.3  An Abstraction for Hittable Objects

现在,多个球体怎么办?虽然使用球体数组的方式很诱人,但一个非常简洁的解决方案是为光线可能碰到的任何物体创建一个"抽象类",并将球体和球体列表都视为可击中的物体。关于该抽象类应该被称为什么有点困扰——如果不考虑"面向对象"编程,称之为"对象"会很好。通常会使用"表面(Surface)"一词,但它的弱点可能在于我们可能需要处理体积(如雾、云等)。"hittable"强调了将它们统一起来的成员函数。我对这些名称都不太满意,但我们将采用"hittable"。

这个"hittable"抽象类将具有一个接受光线作为参数的hit函数。大多数光线追踪器发现给hit添加一个有效的命中区间tmin至tmax很方便,因此只有当tmin<t<tmax时,命中才会"计数"。对于初始光线,这个区间是正数t,但正如我们将看到的,将tmin与tmax定义为区间可以简化我们的代码。一个设计问题是是否在命中物体时计算法线。随着搜索的进行,我们可能会命中更近的物体,而我们只需要最近物体的法线。我将选择简单的解决方案,计算一些我将存储在某个结构中的相关信息。以下是这个抽象类的实现:

#ifndef HITTABLE_H
#define HITTABLE_H

#include "ray.h"

class hit_record {  
public:
point3  p;    
    vec3 normal;    
    double t;
};

class hittable {  
	public:    
	virtual ~hittable() = default;    
	virtual bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& 	rec) const = 0;
};

#endif

这是球体的实现代码:

#ifndef SPHERE_H
#define SPHERE_H
#include "hittable.h"
#include "vec3.h"

class sphere : public hittable {  
public:    
    sphere(point3 _center, double _radius) : center(_center), radius(_radius) {}    
bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const override {
	        vec3 oc = r.origin() - center;
	        auto a = r.direction().length_squared();
	        auto half_b = dot(oc, r.direction());
	        auto c = oc.length_squared() - radius*radius;
	        auto discriminant = half_b*half_b - a*c;
	        if (discriminant < 0) return false;
	        auto sqrtd = sqrt(discriminant);

	        // Find the nearest root that lies in the acceptable range.
	        auto root = (-half_b - sqrtd) / a;
	        if (root <= ray_tmin || ray_tmax <= root) {
	            root = (-half_b + sqrtd) / a;
	        if (root <= ray_tmin || ray_tmax <= root)
	                return false;        
	}        
	rec.t = root;
	rec.p = r.at(rec.t);
	rec.normal = (rec.p - center) / radius;
        return true;
    }  

private:    point3 center;    double radius;
};

#endif

Listing 16: [sphere.h] The sphere class
 

6.4 正面面片与背面面片的区别
    关于法线的第二个设计决是它们是否应该始终指向外部。目前,找到的法线始终指向从中心到交点的方向(法线指向外部)。如果射线从外部与球体相交,法线则指向与射线相反的方向。如果射线从内部与球体相交,法线(始终指向外部)则指向与射线相同的方向。或者,我们可以让法线始终指向与射线相反的方向。如果射线在球体外部,法线将指向外部;但如果射线在球体内部,法线将指向内部

    图7:球体表面法线的可能方向  

我们需要从这些可能性中选择一个,因为最终我们希望确定射线是从表面的哪一侧射出的。这对于在每一侧上都以不同方式渲染的对象非常重要,例如双面纸上的文本或者内外部分明确的玻璃球。

如果我们决定法线始终指向外部,那么在给射线上色时,我们需要确定射线所在的一侧。我们可以通过将射线与法线进行比较来找到答案。如果射线和法线方向相同,射线在物体内部;如果射线和法线方向相反,射线在物体外部。这可以通过计算这两个向量的点积来确定,如果它们的点积为正数,那么射线在球体内部。

if (dot(ray_direction, outward_normal) > 0.0) {    
	// ray is inside the sphere   
...}
 else {    
	// ray is outside the sphere    
...}

   如果我们决定法线始终指向逆向的射线,我们将无法使用点积来确定射线在表面的哪一侧。相反,我们需要存储这个信息:

bool front_face;
if (dot(ray_direction, outward_normal) > 0.0) {
// ray is inside the sphere
normal = -outward_normal;
front_face = false;
} 
else {
    // ray is outside the sphere
    normal = outward_normal;
    front_face = true;
}

     我们可以设置让法线始终从表面“向外”指向,或者始终指向入射射线的反方向。这个决定取决于您是想在几何相交时确定表面的哪一侧,还是在着色时确定。在本书中,我们有比几何类型更多的材质类型,所以我们将选择较少的工作量,并将确定放在几何时间。这只是一种偏好问题,在文献中会看到两种实现方式。

我们在hit_record类中添加了front_face布尔变量。我们还将添加一个函数来解决这个计算:set_face_normal()。为了方便起见,我们假设传递给新的set_face_normal()函数的向量长度为单位长度。我们可以显式地对参数进行归一化,但如果几何代码这样做,效率更高,因为当您对特定几何有更多了解时,通常更容易处理。

class hit_record { 
public:    
point3 p;
vec3 normal;
double t;
 bool front_face;

void set_face_normal(const ray& r, const vec3& outward_normal) {
    // Sets the hit record normal vector.
    // NOTE: the parameter `outward_normal` is assumed to have unit length.
   front_face = dot(r.direction(), outward_normal) < 0;
   normal = front_face ? outward_normal : -outward_normal;
}
};

     Listing 19: [hittable.h] Adding front-face tracking to hit_record

然后我们将表面边界确定添加到该类中:

class sphere : public hittable {
  public:
...
 bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const   {
        ...
        rec.t = root;
        rec.p = r.at(rec.t);
        vec3 outward_normal = (rec.p - center) / radius;
        rec.set_face_normal(r, outward_normal);
	    return true;    
	}
	...
};

 Listing 20: [sphere.h] The sphere class with normal determination

 

6.5 hittable_list

我们有一个名为"hittable"的通用对象,光线可以与其相交。现在我们添加一个类,用于存储一系列"hittable"对象的列表:

#ifndef HITTABLE_LIST_H
#define HITTABLE_LIST_H

#include "hittable.h"
#include <memory>
#include <vector>

using std::shared_ptr;
using std::make_shared;

class hittable_list : public hittable {
 public:
std::vector<shared_ptr<hittable>> objects;
    hittable_list() { }
hittable_list(shared_ptr<hittable> object) { add(object); }

void clear() { objects.clear(); }

void add(shared_ptr<hittable> object) {
   objects.push_back(object);
}

  bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const override {
        hit_record temp_rec;
        bool hit_anything = false;
        auto closest_so_far = ray_tmax;

        for (const auto& object : objects) {
            if (object->hit(r, ray_tmin, closest_so_far, temp_rec)){
                hit_anything = true;
                closest_so_far = temp_rec.t;
                rec = temp_rec;
            }
        }
        return hit_anything;
}
};
#endif

Listing 21: [hittable_list.h] The hittable_list class

 

6.6 一些C++的新特性   

hittable_list 类的代码使用了两个C++特性,如果你平时不是C++程序员可能会遇到困扰:vector和shared_ptr。

shared_ptr<type>是指向已分配类型的指针,具有引用计数语义。每次将其值赋给另一个共享指针(通常是简单赋值),引用计数就会递增。当共享指针超出范围(例如在块或函数的结尾处),引用计数就会递减。一旦计数降至零,对象就会被安全地删除。

通常,共享指针首先使用新分配的对象进行初始化,类似于下面的示例:

shared_ptr<double> double_ptr = make_shared<double>(0.37);
shared_ptr<vec3> vec3_ptr = make_shared<vec3>(1.414214, 2.718281, 1.618034);
shared_ptr<sphere> sphere_ptr = make_shared<sphere>(point3(0,0,0), 1.0);

make_shared<thing>(thing_constructor_params ...) 分配一个新的 thing 类型的实例,使用构造函数参数。它返回一个 shared_ptr<thing>。

由于类型可以通过 make_shared<type>(...) 的返回类型自动推断出来,上述代码可以更简洁地使用 C++ 的 auto 类型推导器表示如下:

auto double_ptr = make_shared<double>(0.37);
auto vec3_ptr   = make_shared<vec3>(1.414214, 2.718281, 1.618034);
auto sphere_ptr = make_shared<sphere>(point3(0,0,0), 1.0);

    在我们的代码中,我们将使用 shared_ptr,因为它允许多个几何体共享一个公共实例(例如,一组使用相同颜色材质的球体),并且使内存管理自动化并更易于理解。

std::shared_ptr 包含在 <memory> 头文件中。

第二个您可能不熟悉的 C++ 特性是 std::vector。它是一个泛型的类似数组的集合,可以存储任意类型的元素。在上面的代码中,我们使用了一个 hittable 指针的集合。std::vector 会在添加更多值时自动扩展:objects.push_back(object) 将一个值添加到 std::vector 成员变量 objects 的末尾。

std::vector 包含在 <vector> 头文件中。

最后,在第21行的 using 语句告诉编译器我们将从 std 库中获取 shared_ptr 和 make_shared,因此我们在引用它们时不需要每次都加上 std:: 前缀。
 


6.7 常见的常量和实用函数

我们需要一些数学常数,在它们自己的头文件中方便地进行定义。现在我们只需要无穷大(infinity),但稍后我们还会在其中添加我们自己对圆周率(pi)的定义,因为我们以后会用到它。关于pi,没有标准的可移植定义,所以我们只需为其定义我们自己的常量。我们将把常见有用的常数和未来的实用函数放在rtweekend.h中,这是我们的主要通用头文件。

#ifndef RTWEEKEND_H
#define RTWEEKEND_H

#include <cmath>
#include <limits>
#include <memory>

// Usings 
Using std::shared_ptr;
using std::make_shared;
using std::sqrt;

// Constants
const double infinity = std::numeric_limits<double>::infinity();
const double pi = 3.1415926535897932385;

// Utility Functions
inline double degrees_to_radians(double degrees) {
return degrees * pi / 180.0;
}

// Common Headers
#include "ray.h"
#include "vec3.h"

#endif

新的main 函数

#include "rtweekend.h"
#include "color.h"
#include "hittable.h"
#include "hittable_list.h"
#include "sphere.h"

#include <iostream>

double hit_sphere(const point3& center, double radius, const ray& r) {
 ... 
}

color ray_color(const ray& r, const hittable& world) {
hit_record rec;
    if (world.hit(r, 0, infinity, rec)) {
       return 0.5 * (rec.normal + color(1,1,1));
}

vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5*(unit_direction.y() + 1.0);
    return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}

int main() {
// Image
auto aspect_ratio = 16.0 / 9.0;
int image_width = 400;

    // Calculate the image height, and ensure that it's at least 1.
int image_height = static_cast<int>(image_width / aspect_ratio);
image_height = (image_height < 1) ? 1 : image_height;
   // World
hittable_list world;
world.add(make_shared<sphere>(point3(0,0,-1), 0.5));
world.add(make_shared<sphere>(point3(0,-100.5,-1), 100));
    
// Camera
auto focal_length = 1.0;
auto viewport_height = 2.0;
auto viewport_width = viewport_height * (static_cast<double>(image_width)/image_height);
auto camera_center = point3(0, 0, 0);

 // Calculate the vectors across the horizontal and down the vertical viewport edges.    auto viewport_u = vec3(viewport_width, 0, 0);
auto viewport_v = vec3(0, -viewport_height, 0);

  // Calculate the horizontal and vertical delta vectors from pixel to pixel.    
auto pixel_delta_u = viewport_u / image_width;
auto pixel_delta_v = viewport_v / image_height;

// Calculate the location of the upper left pixel.
auto viewport_upper_left = camera_center - vec3(0, 0, focal_length) - viewport_u/2 - viewport_v/2;
auto pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);

 // Render
   std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
for (int j = 0; j < image_height; ++j) {
        std::clog << "\rScanlines remaining: " << (image_height - j) << ' ' << std::flush;
        for (int i = 0; i < image_width; ++i) {
            auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);
            auto ray_direction = pixel_center - camera_center;
            ray r(camera_center, ray_direction);
color pixel_color = ray_color(r, world);
               write_color(std::cout, pixel_color);
        }
}
std::clog << "\rDone.                 \n";
}

这会产生一张图片,实际上只是一个显示球体位置及其表面法线的可视化图像。这通常是查看几何模型的任何缺陷或特定特征的绝佳方式。

Image 5: Resulting render of normals-colored sphere with ground


 

6.8 An Interval Class

在我们继续之前,我们将实现一个区间类来管理具有最小值和最大值的实数区间。随着我们的进展,我们将经常使用这个类。 

#ifndef INTERVAL_H
#define INTERVAL_H

class interval {
  public:
double min, max;

interval() : min(+infinity), max(-infinity) {}  // Default interval is empty
interval(double _min, double _max) : min(_min), max(_max) {}

bool contains(double x) const {
        return min <= x && x <= max;
}

bool surrounds(double x) const {
        return min < x && x < max;
}

static const interval empty, universe;
};

const static interval empty   (+infinity, -infinity);
const static interval universe(-infinity, +infinity);

#endif

                 Listing 26: [interval.h] Introducing the new interval class
 

class hittable_list : public hittable {
  public:
...
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
        hit_record temp_rec;
        bool hit_anything = false;
        auto closest_so_far = ray_t.max;
        for (const auto& object : objects) {
            if (object->hit(r, interval(ray_t.min, closest_so_far), temp_rec)) {
                hit_anything = true;
                closest_so_far = temp_rec.t;
                rec = temp_rec;
            }
        }
        return hit_anything;
}
    ...
};

 Listing 29: [hittable_list.h] hittable_list::hit() using interval

class sphere : public hittable {
  public:
...
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
        ...
        // Find the nearest root that lies in the acceptable range.
        auto root = (-half_b - sqrtd) / a;
        if (!ray_t.surrounds(root)) {
            root = (-half_b + sqrtd) / a;
            if (!ray_t.surrounds(root))
                return false;
        }        
	...
}
    ...
};

 Listing 30: [sphere.h] sphere using interval

...
color ray_color(const ray& r, const hittable& world) {
    hit_record rec;
    if (world.hit(r, interval(0, infinity), rec)) {
        return 0.5 * (rec.normal + color(1,1,1));
}
    vec3 unit_direction = unit_vector(r.direction());
    auto a = 0.5*(unit_direction.y() + 1.0);
    return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}
...

                Listing 31: [main.cc] The new main using interval





 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1495314.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

react高阶组件:如何同时兼容class类组件和函数式组件。

场景&#xff1a; 每个页面都要实现分享功能&#xff0c;但是页面有些是用class类&#xff0c;有些又直接是函数式。 方案1&#xff1a; 写2套方法。各自引用。&#xff08;维护不太好&#xff0c;改要改2遍&#xff09; 方案2&#xff1a; 可以封一个 jsx的组件&#xff0c…

服务器cpu占用高没看到进程

现象&#xff1a; 1. 今天连服务器发现root密码被改了&#xff0c;再改回去&#xff0c;登录发现服务器很卡&#xff0c;top查看&#xff0c;可用的cpu为0&#xff0c;但是没看到明显的进程&#xff0c;很显然中了病毒 2. 发现crontab -l有异常的定时计划&#xff0c;给删除掉 …

DailyNotes个人笔记管理工具

DailyNotes 是记录笔记和跟踪任务的应用程序&#xff0c;使用markdown进行编辑 部署 下载镜像 docker pull m0ngr31/dailynotes创建目录并授权 mkdir -p /data/dailynotes/config_dir chmod -R 777 /data/dailynotes启动容器 docker run -d --restart always --name mynot…

【Web安全靶场】upload-labs-master 1-21

upload-labs-master 其他靶场见专栏… 文章目录 upload-labs-masterPass-01-js前端校验Pass-02-MIME校验Pass-03-其他后缀绕过黑名单Pass-04-.hatccess绕过Pass-05-点空格点代码逻辑绕过Pass-06-大小写绕过Pass-07-空格绕过Pass-08-点号绕过Pass-09-::$DATA绕过Pass-10-点空格…

2024年腾讯云8核16G18M服务器租用价格1668元15个月

2024年腾讯云8核16G18M服务器租用价格1668元15个月&#xff0c;270GB SSD云硬盘&#xff0c;3500GB月流量。 一张表看懂腾讯云服务器租用优惠价格表&#xff0c;一目了然&#xff0c;腾讯云服务器分为轻量应用服务器和云服务器CVM&#xff0c;CPU内存配置从2核2G、2核4G、4核8…

【开源】JAVA+Vue.js实现学校热点新闻推送系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 新闻类型模块2.2 新闻档案模块2.3 新闻留言模块2.4 新闻评论模块2.5 新闻收藏模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 新闻类型表3.2.2 新闻表3.2.3 新闻留言表3.2.4 新闻评论表3.2.5 新闻收藏表 四、系统展…

Python与FPGA——全局二值化

文章目录 前言一、Python全局128二、Python全局均值三、Python全局OTSU四、FPGA全局128总结 前言 为什么要进行图像二值化&#xff0c;rgb图像有三个通道&#xff0c;处理图像的计算量较大&#xff0c;二值化的图像极大的减少了处理图像的计算量。即便从彩色图像转成了二值化图…

HarmonyOS应用开发-环境搭建(windows环境)

官网地址&#xff1a;链接 DevEco Studio 3.1.1 Release&#xff1a;下载地址 1、安装DevEco Studio 直接安装即可 2、配置开发环境 1.运行已安装的DevEco Studio&#xff0c;首次使用&#xff0c;请选择Do not import settings&#xff0c;单击OK。 2.安装Node.js与ohpm。注…

STC89C52串口通信详解

目录 前言 一.通信基本原理 1.1串行通信与并行通信 1.2同步通信和异步通信 1.2.1异步通信 1.2.2同步通信 1.3单工、半双工与全双工通信 1.4通信速率 二.串口通信简介 2.1接口标准 2.2串口内部结构 2.3串口相关寄存器 三.串口工作方式 四.波特率计算 五.串口初始化步骤 六.实验…

【go语言开发】gorm库连接和操作mysql,实现一个简单的用户注册和登录

本文主要介绍使用gorm库连接和操作mysql&#xff0c;首先安装gorm和mysql依赖库&#xff1b;然后初始化mysql&#xff0c;配置连接池等基本信息&#xff1b;然后建表、完成dao、controller开发&#xff1b;最后在swagger中测试 文章目录 前言安装依赖库数据库初始化账号注册和登…

Hadoop运行搭建——系统配置和Hadoop的安装

Hadoop运行搭建 前言&#xff1a; 本文原文发在我自己的博客小站&#xff0c;直接复制文本过来&#xff0c;所以图片不显示(我还是太懒啦&#xff01;)想看带图版的请移步我的博客小站~ Linux镜像&#xff1a;CentOS7 系统安装&#xff1a;CentOS安装参考教程 系统网卡设置…

在win10中下载桌面版的docker并在docker中搭建运行基于linux的容器

在win10中下载桌面版的docker 1.背景 在很多时候需要linux系统部署项目&#xff0c;在win10中安装虚拟机并在虚拟机中安装linux系统比较繁琐&#xff0c;可以利用win10自带的hyper-v的虚拟机管理工具&#xff0c;打开该虚拟机管理工具&#xff0c;安装docker&#xff0c;并在…

【排序】希尔排序

一、思想 希尔排序&#xff0c;由D.L. Shell于1959年提出&#xff0c;是基于插入排序的一种改进算法。它的主要思想是将待排序的序列分割成若干个子序列&#xff0c;这些子序列的元素是相隔一定“增量”的。然后对每个子序列进行直接插入排序。随着增量的逐步减小&#xff0c;…

数学建模【整数规划】

一、整数规划简介 整数规划其实是线性规划和非线性规划的一个特殊情况&#xff0c;即有的变量取值只能是整数&#xff0c;不能是小数。这时候就需要一个新的函数来解决问题。 对于整数规划&#xff0c;分为线性整数规划和非线性整数规划 线性整数规划&#xff1a;MATLAB可进…

gitlab仓库迁移至bitbucket

0. 场景描述 假设已有一个gitlab仓库&#xff1a;ssh://xxx_origin.git&#xff0c;想要把这个仓库迁移至bitbucket上。 默认gitlab和bitbucket的SSH key都已添加。 1. 新建bitbucket仓库 在bitbucket上创建新的仓库&#xff0c;并复制url地址。假设为&#xff1a; https:/…

Leetcode 26. 删除有序数组中的重复项 java版。 java解决删除重复数组元素并输出长度

1. 官网链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 2. 题目描述&#xff1a; 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该…

一个数据库表格缺少自动增加的字段导致添加一条数据失败

一个数据库表格缺少自动增加的字段导致添加一条数据失败。最近要整理出一个cms网站源程序&#xff0c;因此新建了一个目录&#xff0c;将需要的文件复制到该目录。复制好以后&#xff0c;试用的时候发现添加留言失败。经过数小时的查找原因&#xff0c;最后找到原因&#xff0c…

修复通达OA 百度ueditor 文件上传漏动

前些日子&#xff0c;服务器阿里云监控报警&#xff0c;有文件木马文件&#xff0c;因为非常忙&#xff0c;就没及时处理&#xff0c;直接删除了木马文件了事。 谁知&#xff0c;这几天对方又上传了木马文件。好家伙&#xff0c;今天不花点时间修复下&#xff0c;你都传上瘾了…

苍穹外卖学习-----2024/03/04

1.公共字段填充 代码在这里

【python--读取csv文件统计店铺有关信息】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Python &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; python练习题 读取csv文件统计店铺有关信息 读取csv文件统计店铺有关信息 import csv import osdef get_…